인덱스 탐색 과정에 비효율이 있다면 이를 제거하기 위하여 노력해야 한다.
옵티마이저는 인덱스를 수직으로 탐색하고 나서 리프 페이지를 수평으로 읽어 나갈 때 시퀀셜 엑세스 방식을 사용한다.
이 엑세스 방식이 랜덤 엑세스보다 상대적으로 빠르기는 하지만 불필요하게 넓은 범위를 엑세스하면
인덱스 탐색 효율에 문제가 생긴다.
인덱스 탐색 효율을 개선하고자 할 때, 기존 인덱스에 칼럼을 추가하거나 클러스터형 인덱스로 변경하는 등의 방법은
별로 도움되지 않는다.
인덱스 탐색 효율에 큰 영향을 미치는 것은 '인덱스 키 칼럼 순서'와 '검색 조건에 사용된 연산자'이다.
1. 모든 칼럼에 equal 조건을 사용했을 때
아래와 같은 테이블이 존재한다고 가정해보자.
ALTER TABLE dbo.TBLINSA_SH_TEST ADD CONSTRAINT PK__TBLINSA_SH_TEST__OID PRIMARY KEY (oid)
SELECT * FROM dbo.TBLINSA_SH_TEST WITH(NOLOCK)
IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT 인덱스와 IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID 인덱스를 구성하였다.
인덱스는 구성이 같으나, 순서가 다르다는 점에 주목해야 한다.
CREATE INDEX IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT ON dbo.TBLINSA_SH_TEST (op_id,chg_dt)
CREATE INDEX IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID ON dbo.TBLINSA_SH_TEST (chg_dt,op_id)
검색 조건은 같지만 서로 다른 인덱스를 사용하게끔 힌트를 추가한 두 쿼리를 작성 하였다.
SELECT * FROM dbo.TBLINSA_SH_TEST WITH(NOLOCK,INDEX(IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT))
WHERE op_id = 'chchoing'
AND chg_dt = '2015-07-14 20:51:50.747'
SELECT * FROM dbo.TBLINSA_SH_TEST WITH(NOLOCK,INDEX(IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID))
WHERE op_id = 'chchoing'
AND chg_dt = '2015-07-14 20:51:50.747'
아래를 보면 두 쿼리의 성능차에 대해서 알 수 있다.
두 쿼리의 비용은 각각 50%로 똑같다.
두 쿼리의 성능이 같은 이유를 알아보자. 인덱스 구성 칼럼과 검색조건이 같으므로 북마크 룩업 과정은 차이가 없다.
인덱스 탐색 과정만 살펴보면 된다.
어느 인덱스를 탐색하더라도 엑세스 범위는 같다.
즉 인덱스 탐색의 전체 일량에도 아무런 차이가 없다.
모든 인덱스 칼럼에 equal 조건을 사용하면 인덱스를 가장 효율적으로 탐색하게 된다.
만약 인덱스 키 칼럼을 항상 equal 조건으로 검색한다면 인덱스 칼럼 순서를 심각하게 고민할 필요가 없다.
2. 선행 칼럼에 between 조건을 사용했을 때
이번에는 앞에서 생성한 인덱스를 그대로 두고 쿼리만 조금 변경하였다, (between 사용)
SELECT * FROM dbo.TBLINSA_SH_TEST WITH(NOLOCK,INDEX(IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT))
WHERE op_id = 'chchoing'
AND chg_dt BETWEEN '2010-07-01' AND '2022-01-01'
SELECT * FROM dbo.TBLINSA_SH_TEST WITH(NOLOCK,INDEX(IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID))
WHERE op_id = 'chchoing'
AND chg_dt BETWEEN '2010-07-01' AND '2022-01-01'
쿼리의 비용 자체가 많이 나는 것을 볼 수 있다 똑같이 인덱스를 통하여 SEEK 해서 데이터를 가져오고 있지만
SEEK 하는 부분의 I/O 자체가 다른것을 볼 수 있다.
현재 테이블에서는 데이터가 많이 없어서 큰 차이로 보여지지 않지만 만약에 데이터가 수십만건 수백만건이 된다면
I/O 가 4.5 배가 차이난다는것은 매우 큰 차이이다.
이렇게 큰 성능 차이가 발생한 이유를 알아보기 위해
도식화한 두 쿼리의 인덱스 탐색 과정을 비교해서 나타냈다.
두 쿼리의 인덱스 엑세스 범위에 큰 차이가 있다.
왼쪽 IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT 인덱스는 필요한 범위만 엑세스 하여 데이터를 추출한다.
인덱스 선두 칼럼에 equal 조건을 사용 했으므로, 두 번째 컬럼 (=chg_dt)이 인덱스 엑세스 범위를 결정하게 된다.
보다시피 IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT 인덱스는 탐색하는 과정에서 비효율이 전혀 없다.
반면에 오른쪽을 보면, IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID 인덱스에서 불필요하게 넓은 범위를 엑세스한다.
인덱스 선두 칼럼인 chg_dt 칼럼에 between 조건을 사용했으므로 op_id 칼럼은 필터 역할 밖에 할 수 가 없다.
IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID 인덱스는 탐색하는 과정에서 검색조건에 해당하지 않은 로우까지
모두 읽고 버리는 비효율이 있다.
1) IDX__TBLINSA_SH_TEST__OP_ID__CHG_DT 인덱스(op_id,chg_dt)
1건을 읽고 실제 1건을 조회함
2) IDX__TBLINSA_SH_TEST__CHG_DT__OP_ID 인덱스(chg_dt,op_id)
4109건을 읽고 실제 1건을 조회함
인덱스 선두 칼럼에 equal 조건을 사용하고, 두 번째 칼럼에 between 등의 조건을 사용하면,
두 번째 칼럼에 대한 검색 조건이 인덱스 탐색 일량을 결정한다.
인덱스 선두 칼럼에 between 등의 조건을 사용하면, 첫 번째 (=선두) 칼럼이 인덱스 탐색 일량을 결정하고
두 번째 칼럼은 필터링 정도의 역할만 하게 된다.
즉, 두 번째 칼럼 부터는 인덱스 탐색 일량을 줄이는 데 별 도움이 되지 않는다.
'SQL Tuning' 카테고리의 다른 글
[MSSQL] MERGE JOIN (Sorted Merge Join) (0) | 2022.04.07 |
---|---|
[MSSQL] NL JOIN(Nested Loops Join) (2) | 2022.04.06 |
북마크 룩업 최소화 기법(3) - Covered Index (0) | 2022.01.29 |
북마크 룩업 최소화 기법(2) - Clustered Index (0) | 2022.01.28 |
북마크 룩업 최소화 기법(1) - 결합 인덱스 (0) | 2022.01.24 |