이전까지는 인덱싱을 써볼 생각을 안했다.
성능을 생각하기 보다는 기능 구현에 초점을 두고 개발을 했으니까…
애초에 데이터도 얼마 없으니 DB 인덱싱에 대해 생각이 들지 않고, 전공 수업에서 들었던
B tree 써서 해주는거라고만 알고 있었는데, 인턴을 하며 많은 데이터를 보게 되었다.
데이터가 많아지니, 당연히 시간도 오래 걸리는걸 체감하게 되어, 인덱싱을 하고 싶어졌다.
일반적으로 인덱스를 설계한다면 WHERE
절에 대한 인덱스를 생각하지만, 실제로는 쿼리 전체에 대한 인덱스 설계가 필요합니다.
인덱스는 데이터를 효율적으로 찾는 방법이지만, 이를 잘 활용한다면 실제 데이터까지 접근하지 않고도 데이터를 찾아올 수 있다.
쿼리를 충족시키는데 필요한 모든 데이터를 가지고 있는 인덱스를 커버링 인덱스(Covering Index)라 한다.
실제 데이터에 접근하지 않기 때문에, 더 빠르게 실행됩니다.
# 커버링 인덱스 X
EXPLAIN
SELECT *
FROM board.member m
WHERE m.member_id < 10;
# 커버링 인덱스 O
EXPLAIN
SELECT m.memeber_id
FROM board.member m
WHERE m.member_id < 10;
컴포지트 인덱스(Composite Index)는 다중 컬럼 인덱스라고도 불립니다. 2개 이상의 컬럼으로 이루어진 인덱스 입니다.
인덱스는 구성된 순서에 영향을 받으며, (a,b,c)로 인덱스를 구성한다면, a에 의해 b가 정렬되고, a, b에 의해 c가 정렬되기 때문에 순서에 영향을 받습니다.
where
, group by
를 사용할 때 인덱스 구성 순서와 동일하게 사용해야 합니다.
# 인덱스 작용 O
WHERE a = 10 AND c = 10
WHERE a = 10 AND b = 10
WHERE a = 10 AND b = 10 AND c = 10
# 인덱스 작용 X
WHERE b = 10
WHERE b = 10 AND c = 10
WHERE c = 10
쿼리문을 작성할 때도 인덱스가 적용이 되는지 확인해야 한다.
인덱스를 설정했어도, 인덱스를 타지 않는 쿼리문을 사용한다면 인덱스를 설정하지 않은 것과 다를 바 없다.
select * from table where LOWER(name) ='word';
select * from table where idx - 1 = 5;
다음과 같이 인덱스가 잡힌 컬럼을 변형을 가하여 WHERE문을 작성한다면 데이터베이스는 인덱스를 이용하지 않는다.
select * from table where age = '30'
다음과 같이 age는 Integer 라고 가정했을 때, String 값을 넣어서 WHERE문을 작성한다면 인덱스를 이용하지 않는다. 따라서 정확한 데이터 값과 데이터 타입을 넣어줘야 한다.
SELECT * FROM users WHERE NOT age = 30;
NOT일 경우에도 인덱스를 타긴 타지만, 일반적으로, NOT에 사용된 값이 아닌 데이터의 비율이 높은 경우가 많기 때문에 인덱스를 타지 않는 경우가 많다. 이러한 경우 DBMS는 인덱스를 사용하는 것보다 전체 테이블을 스캔하는 것(Full Table Scan)이 더 효율적이라고 판단할 수 있다.
마찬가지로 IN일 경우에도, IN에 포함된 데이터들의 비율이 매우 높다면 FULL SCAN을 하는 것이 낫다고 DBMS가 판단하면 인덱스를 타지 않는다.
select * from table where name like '%word';
문자열로 이루어진 값을 인덱스로 잡았을 때, %가 앞쪽에 사용되면 정렬 순서를 사용할 수 없으므로 테이블 FULL SCAN이 이루어진다. word 앞에 뭐가 올 지 모르기 떄문에 인덱스를 사용할 수 없다.
select * from table where name like 'word%';
이런 경우는 문자열 정렬 순서를 그대로 이용할 수 있기 때문에 인덱스를 탈 수 있다.
결합 인덱스 = 컬럼1,컬럼2
select * from table where 컬럼2
복합 인덱스는 인덱스를 지정하는 컬럼1과 컬럼2 값을 합쳐서 인덱스를 생성하기 때문에 순서가 바뀐다면 인덱스를 적용할 수 없다. 하지만 복합 인덱스 설정을 (컬럼1, 컬럼2)로 하고, WHERE절에 컬럼1을 지정한다면 인덱스를 사용할 수 있다.
SELECT * FROM users WHERE age = 25 OR city = 'Seoul';
age와 city 각각 인덱스가 설정되었다고 해도, OR 조건이 있는 쿼리는 복잡도가 증가할 수 있기 때문에, DBMS는 전체 테이블을 스캔하는 것이 더 빠르다고 판단할 수 있다. 물론 인덱스를 이용할 수도 있지만, 안하는 경우가 대부분이다.
SELECT * FROM table_name WHERE name = NULL; -- 인덱스 활용X
SELECT * FROM table_name WHERE age IS NULL; -- 인덱스 활용X
SELECT * FROM table_name WHERE age > 0; -- 인덱스 활용O
NULL 값은 인덱스에 저장되지 않기 때문에, NULL을 사용하면 인덱스를 이용할 수 없다.
select * from table where name ='word' and id ='elky';
인덱스가 name 과 id로 2개가 있을 경우 id나 name 인덱스 중 하나가 선택될 수도 있고, 둘 다 선택될 수도 있다. 어떤 방식으로 선택하는냐가 속도에 중요할 수도 있다. 즉 실행 계획을 추적해서 원하는 결과가 나오도록 관리가 필요하다. 따라서 Optimizer가 어떻게 계획을 세우고 실행하냐에 따라서 실행 시간이 달라질 수 있다.