sqlleader.co.kr 의 Q&A 게시판에 올린 글임.
(IBM 메인프레임에서 내려오는 데이터에 대해서는 속수무책인 것 같아 보이긴 하는데... 일반적인 환경에서라면 SSIS 상당히 훌륭하다. 잘 활용하면 배치잡 튜닝에 좋은 효과를 볼 수 있을 듯...)
--------------------------
다음과 같은 형태의 쿼리가 있습니다.
insert into remote_server.dbo.TARGET_TABLE (col1 ~ coln)
select col1 ~ coln
from MAIN_BIG_TABLE m (1000만건)
inner join SUB_BIG_TABLE1 s1 (1000만건)
inner join SUB_BIG_TABLE2 s2 (1000만건)
inner join SUB_TINY_TABLE1 t1 (5000건)
inner join SUB_TINY_TABLE2 t2 (5000건)
inner join SUB_TINY_TABLE3 t3 (5000건)
where m.col1 = 'XXX'
and s1.col1 = 'XXX'
and s2.col1 = 'XXX'
(이하생략)
위의 쿼리를 돌리면 300만건이 insert 가 됩니다. 즉, s1, s2, t1, t2, t3 을 inner join 하면서 700만건이 떨어져 나가는 것이죠. 현재 돌고 있는 배치잡에서 위 쿼리가 가장 시간이 많이 걸리는 부분이었습니다. 대략 30분 정도가 걸렸죠.
이걸 SSIS 를 이용해서 개선을 해보았습니다. 다음 두가지 방식으로 시도를 했습니다.
1. 조회변환을 여러번 이용
MAIN_BIG_TABLE 은 OLE DB 원본으로 읽어오고 SUB테이블들을 모두 조회 변환으로 읽어왔습니다. SSIS 엔진이 도는 서버의 사양이 매우 좋아서 조회 변환에 대한 메모리 제한은 두지 않았습니다. 원본 서버와 대상 서버에 대해서 dbcc dropcleanbuffers, dbcc freeproccache 를 모두 수행해서 반복테스트시 캐싱이 미치는 영향은 제거했습니다. 결과는 다음과 같습니다.
5번 반복 테스트 결과 대상 서버에 OLE DB 대상으로 BULK INSERT 하는데 평균 11분
메모리 사용량 : 최고 1.4GB
2. BIG_TABLE간의 inner join 을 SSIS의 병합 조인으로 대체
BIG_TABLE 3개는 마침 clustered index 가 동일한 컬럼에 만들어져 있었습니다. 그리고 이 컬럼을 join key 로 사용하죠. (사실 BIG_TABLE 3개는 MAIN을 기준으로 1:1 mandatory 관계에 있는 테이블들입니다. 그냥 하나로 합쳐도 될텐데.... 모델링상 문제가 좀 있죠...) 따라서 clustered index scan 을 유도하고 order by 를 줘도 DB에 order by 부하 없이 join key 에 대해서 동일한 순서로 데이터가 넘어옵니다. 이는 실행계획상에서도 확인을 했습니다. BIG_TABLE 이 이미 정렬되어 있는 상태로 넘어오므로 BIG_TABLE 들간의 join 을 병합 조인으로 대체했습니다. SMALL_TABLE 들에 대해서는 그대로 조회 변환을 사용했습니다. 마찬가지로 원본, 대상 서버의 버퍼 풀과 프로시저 캐시는 비웠습니다.
5회 반복 테스트 결과 평균 7분의 시간이 걸렸습니다.
메모리 사용량 : 최고 900MB
아무래도 조회 변환은 메모리 사용 제한이 없으면 BIG_TABLE의 모든 데이터를 들고 있어야 하므로 메모리 사용이 많을 수 밖에 없는 반면, 병합 조인은 조인이 완료된 rowset 에 대해서는 더이상 메모리에 들고 있을 필요가 없기 때문에 data 가 흘러가면서 메모리 사용을 해제할 수 있기 때문에 이런 결과가 나온게 아닌가 싶습니다. 그리고 원본 서버에서 master..sysprocesses 을 확인해보니까 병합 조인으로 했을 때는 3개의 BIG_TABLE의 데이터를 동시에 읽어가는 것을 확인했습니다. 하지만 조회 변환으로 하면 아무래도 순차적으로 처리하다 보니까 최고 2개의 BIG_TABLE에 대해서만 select 가 들어오더군요.
위의 테스트를 통해서 배운 바를 정리하자면,
"매우 큰 테이블의 join key 들에 대해서 데이터가 이미 정렬되어 있는 경우, 병합 조인을 사용하는 것이 메모리 사용과 수행 시간면에서 유리하다."
가 되겠습니다. 중요한 것은 "데이터가 이미 정렬되어 있는 경우" 가 아닐까 합니다.
이렇게 하든 저렇게 하든 3배 ~ 4배 정도 속도가 빨라졌으니 대단히 만족하고 있습니다. ^_^
Posted by maceo