PostgreSQL 인덱스가 안 타는 5가지 이유
긴 글, 핵심만 먼저 — 이 글을 3줄로 정리해드려요.
인덱스를 만들었는데 EXPLAIN을 보면 Seq Scan이 나온다. 당황하기 전에 아래 다섯 가지를 순서대로 확인하자.
1. 테이블이 작다
수천 행짜리 테이블은 인덱스를 타는 것보다 전체 스캔이 싸다. 플래너가 옳다. 운영 데이터 규모로 테스트하지 않으면 인덱스 검증은 의미가 없다.
2. 함수를 씌웠다
-- 인덱스 못 탐
WHERE lower(email) = 'a@b.com'
-- 표현식 인덱스를 만들면 탐
CREATE INDEX idx_users_email_lower ON users (lower(email));
컬럼에 함수나 연산을 적용하면 일반 인덱스는 무용지물이다. 표현식 인덱스를 만들거나 쿼리를 바꿔야 한다.
3. 타입이 다르다
varchar 컬럼을 숫자와 비교하면 암묵적 캐스팅이 일어나면서 인덱스를 못 쓴다. ORM이 파라미터 타입을 애매하게 보낼 때도 생긴다. EXPLAIN에서 ::text 같은 캐스트가 보이면 의심하자.
4. 선두 컬럼이 빠졌다
복합 인덱스 (a, b)는 WHERE b = ? 만으로는 못 쓴다. 왼쪽 접두어 규칙이다. 쿼리 패턴을 보고 컬럼 순서를 정해야 하고, 반대 패턴이 많으면 인덱스를 따로 하나 더 만드는 게 맞다.
5. 통계가 낡았다
대량 INSERT/DELETE 직후에는 플래너 통계가 현실과 다르다. ANALYZE 한 번이면 해결되는 문제로 몇 시간 삽질할 수 있다. autovacuum이 따라오지 못하는 배치 작업 뒤에는 수동 ANALYZE를 습관화하자.
정리
인덱스 문제의 진단 도구는 결국 EXPLAIN (ANALYZE, BUFFERS) 하나다. 추측하지 말고 실행 계획을 읽자. rows 추정치와 실제 값의 차이가 크면 통계 문제, 캐스트가 보이면 타입 문제다.