이번 글에서는 2개 이상의 컬럼을 묶어서 사용하는 복합 Index의 동작 방식에 대해서 알아보도록 하겠다.
Index를 사용하는 이유는 당연하게도 데이터 조회의 성능 향상을 위함이다. 복합 Index를 사용하는 이유 또한 데이터를 조회할 때, 빈번하게 같이 조회되는 컬럼들에 대해서 조회 성능을 향상시키기 위해서이다.
성능 향상을 가져오는 것은 알겠지만, 각 컬럼에 Index를 거는 것과는 어떠한 차이가 있을까?
각각의 컬럼에 따로 인덱스가 걸려있다면, MySQL 옵티마이저는 두 가지 전략 중 하나를 선택한다.
반면 복합 인덱스는 선행 컬럼 기준으로 정렬된 상태에서 후행 컬럼까지 함께 저장하기 때문에, 두 조건을 하나의 인덱스 스캔으로 처리할 수 있다. 이로 인해 스캔 범위가 줄어들고 더 나은 조회 성능을 제공할 수 있다.
CREATE TABLE user_name (
id BIGINT NOT NULL,
last_name VARCHAR(20) NOT NULL,
first_name VARCHAR(20) NOT NULL,
PRIMARY KEY (id),
INDEX full_name (last_name,first_name)
);
먼저 이렇게 '이름'을 저장하는 테이블이 있다고 가정해보자. last_name과 first_name을 기록하는 컬럼이 있고, last_name, first_name 순서대로 복합 인덱스가 걸려있는 상태이다.
아래부터는 위 테이블을 예를 들어서 이야기하도록 하겠다.
복합 인덱스는 명시된 컬럼들의 순서대로 데이터를 정렬시켜서 저장한다.
즉, user_name테이블의 데이터는 last_name 오름차순으로 정렬이 되고, 같은 last_name을 갖고 있는 데이터들은 first_name 오름차순으로 또 다시 정렬되는 것이다.
MySQL은 인덱스의 가장 왼쪽 컬럼부터 순서대로 포함하는 경우에만 인덱스를 사용할 수 있다. 이를 leftmost prefix 규칙이라고 한다.
| 쿼리 | 인덱스 사용 여부 |
|---|---|
WHERE last_name = 'Kim' | ✅ 사용 가능 |
WHERE last_name = 'Kim' AND first_name = 'Juhyeon' | ✅ 사용 가능 |
WHERE last_name = 'Kim' AND first_name >= 'J' AND first_name < 'K' | ✅ 사용 가능 |
WHERE first_name = 'Juhyeon' | ❌ 사용 불가 |
WHERE last_name = 'Kim' OR first_name = 'Juhyeon' | ❌ 사용 불가 |
복합 인덱스를 생성할 때 컬럼 순서는 매우 중요하다. 어떤 기준으로 순서를 정하면 좋을까?
기준 1: 쿼리 패턴 (가장 중요)
가장 우선시해야 할 기준은 실제 쿼리 패턴이다. 단독으로 자주 조회되는 컬럼을 앞에 두어야 한다.
예를 들어 last_name만으로 조회하는 쿼리가 자주 있다면, (last_name, first_name) 순서가 적합하다. 반대로 (first_name, last_name) 순서라면 first_name 단독 조회는 인덱스를 활용할 수 있지만, last_name 단독 조회는 인덱스를 사용하지 못한다.
대표적인 예시로 '대분류', '소분류'와 같은 컬럼이 있는 경우 '대분류' 선행 컬럼으로 복합 인덱스를 생성해야한다.
기준 2: 카디널리티 (선택적 고려)
카디널리티(Cardinality)란 해당 컬럼에 존재하는 고유한 값의 수를 의미한다. 카디널리티가 높을수록 중복이 적고, 인덱스가 데이터를 더 세밀하게 구분할 수 있다.
쿼리 패턴의 관점과 카디널리티의 관점은 충돌할 수 있는데, 두 기준이 충돌할 때는 쿼리 패턴을 우선으로 하는 것이 좋다. 카디널리티가 높아도 단독 조회가 필요 없는 컬럼이라면 굳이 앞에 둘 이유가 없기 때문이다. 즉, 실제 서비스 쿼리를 분석한 후 인덱스 순서를 결정하는 것이 가장 바람직하다.
복합 인덱스에 대해서 최종적으로 정리해보자면 이렇게 정리할 수 있을 것 같다.
즉, 다중 컬럼 인덱스를 사용할 때는 상대적으로 대분류에 해당하는 컬럼이 앞쪽으로 오는 것이 바람직하다. 다시 말하면, 데이터의 중복도가 높은 컬럼이 앞쪽으로 오는 것이 바람직하다고 할 수 있다.
이거 카디널리티가 높은 순서대로 인덱싱하는게 맞는데
대분류는 카디널리티가 낮으므로 .. 뒤에 놔야 합니다.?