길다란 복합 인덱스가 능사가 아니다?
작성: 박노철(maceo.park@gmail.com)
오늘 기존 테이블에 대한 index tuning 을 하다가 발견한 현상.
테이블 T1 사이즈 : 1500만건
T1에는 클러스터드 인덱스가 있다고 가정하자.
select * from T1 where a = 1 and b = 1 and c = 1 and d = 1
이라는 쿼리가 있다고 치자. 이럴때는 보통
create index idx__a_b_c_d on T1 (a,b,c,d)
이렇게 잡는다.
그런데 다음 쿼리가 들어왔다.
select * from T1 where c = 1 and d = 1
그러면 c,d 에
create index idx__c_d on T1 (c,d)
복합인덱스를 잡으면 된다.
이제 우리는 a,b,c,d 와 c,d 에 대한 인덱스 두개를 가지고 있다. 일반적으로 위와 같이 해결 하지만 다른 방법도 생각해볼 수 있다. where조건을 구성하는 컬럼을 분해해서 다음과 같이 인덱스를 구성하는 것이다.
create index idx__a_b on T1 (a,b)
create index idx__c_d on T1 (c,d)
좀 이상하지 않은가?????
이렇게 하면 where a=1 & b=1 & c=1 & d=1 에 대해서 idx__a_b를 타게 될테니 idx__a_b_c_d가 있을 때보다 읽기수가 대폭 증가할 것이라고 예상이 된다. idx__a_b를 탄 후에 bookmark lookup 을 해서 c,d를 체크해야 할 테니. 하지만 이런 경우 SQL Server는 똑똑하게 동작한다. 옵티마이저는 bookmark lookup 의 비용이 과도하다고 판단하면 T1의 두개의 인덱스 idx__a_b와 idx__c_d를 동시에 Index Seek을 해서 join 을 해버린다. maxdop 가 높고 시스템 가용자원이 남아서 병렬 계획을 수립할 가능성이 높으면 더더욱 두개의 인덱스를 병렬로 seek하는 방향으로 실행계획이 유도된다. join 할 때는 각각의 인덱스 끝에 붙어 있는 클러스터드 인덱스 키를 이용하는 것 같다. a,b의 선택성이 좋아서 bookmark lookup 비용이 과도하지 않으면 저렇게 동작하지 않는 것 같다.
인덱스를 위와 같이 잡을 때의 이점은 명백하다. c,d 컬럼에 대해서 중복 인덱스를 잡을 필요가 없어진다. 그렇다고 해서 idx__a_b_c_d가 있을 때보다 읽기수가 엄청나게 증가하는 것은 아니다. 물론 늘어나긴 하지만 감당할만한 수준이다.
결론: 액세스가 빈번하지는 않으나 테이블 사이즈가 매우 커서 공간을 절약할 필요가 있는 경우 where조건을 구성하는 컬럼을 분해해서 각각의 index를 잡는 전략을 고려해볼 수 있다.
Posted by maceo