LabHub

PostgreSQL 심화 — 트랜잭션·인덱스·JSONB·파티션 · 인덱스 설계 · 이론

인덱스 설계 — 질의에 맞춰 고른다

LabHub 에서 이어서 보기

한 줄 요약

인덱스는 '만들면 빨라지는 것' 이 아니라 질의의 모양에 맞춰 고르는 것이다. 복합 인덱스의 열 순서, 부분·표현식·커버링 인덱스, jsonb 와 시계열에 맞는 인덱스 종류를 모르면, 인덱스를 잔뜩 만들고도 순차 스캔을 보게 된다.

왜 이게 필요했나

느린 질의에 인덱스를 하나 만들었는데 계획이 그대로다. 또 하나 만든다. 여전히 순차 스캔이다. 인덱스는 다섯 개가 됐고 쓰기는 느려졌는데 읽기는 빨라지지 않았다.

원인은 인덱스가 질의의 모양과 안 맞기 때문이다. lower(email) 로 찾는데 email 에 인덱스를 걸었고, 두 열을 함께 거르는데 단일 열 인덱스 둘을 만들었고, jsonb 담김을 B 트리로 받으려 했다. 인덱스는 종류와 열 순서가 질의와 맞아떨어질 때만 쓰인다.

어떻게 동작하나

B 트리 인덱스는 값으로 정렬된 구조다. 그래서 등호와 범위 탐색에 쓰이고, 열에 함수를 씌우면 그 정렬이 쓸모없어진다. 여기서 실무의 설계 규칙이 나온다.

종류를 질의에 맞춘다

값의 종류마다 맞는 인덱스가 다르다.

| 대상 | 맞는 인덱스 | 이유 |
| --- | --- | --- |
| jsonb 담김(@>), 배열 포함 | GIN | 문서 안의 키·값을 역색인으로 |
| 본문 전문 검색(to_tsvector) | GIN | 단어를 역색인으로 |
| 삽입 순서와 같은 시계열 | BRIN | 블록 범위만 저장해 B 트리의 수백 분의 일 크기 |

인덱스는 공짜가 아니다. 쓰기마다 갱신되고 자리를 차지한다. 그래서 안 쓰이는 인덱스는 부채다. pg_stat_user_indexesidx_scan 이 0인 인덱스는 아무도 안 쓰면서 쓰기만 느리게 한다.

실무에서 진짜 중요한 것

첫째, 인덱스는 질의를 보고 만든다. 열 하나를 보고 만드는 게 아니라, WHERE·ORDER BY·GROUP BY 에 무엇이 오는지를 보고 종류와 열 순서를 정한다. EXPLAIN (FORMAT JSON) 으로 계획이 정말 그 인덱스를 쓰는지 확인하기 전에는 끝난 게 아니다.

둘째, 큰 결과에는 인덱스가 손해일 수 있다. 조건이 전체의 상당 비율을 통과시키면, 인덱스로 힙을 랜덤 방문하는 비용이 순차 스캔보다 커진다. 옵티마이저가 인덱스를 안 쓰는 것은 대개 옳은 판단이다.

셋째, 안 쓰이는 인덱스는 지운다. idx_scan 을 정기적으로 보고, 아무도 안 쓰는 인덱스는 쓰기 비용만 무는 부채이니 걷어 낸다. 다음 실습에서 20만 행 위에서 이 설계들을 직접 만들고 계획으로 확인한다.