WHERE에 함수를 씌우면 인덱스가 죽는다: 표현식 인덱스와 생성 컬럼
LOWER(email)이나 DATE(created_at) 조건이 인덱스를 못 타는 이유를 짚고, PostgreSQL 표현식 인덱스, MySQL 8 함수 인덱스와 생성 컬럼, SQLite 표현식 인덱스로 다시 인덱스를 태우는 방법과 함정을 정리했다.
실행 계획을 읽고 느린 쿼리를 빠르게 만든 기록
LOWER(email)이나 DATE(created_at) 조건이 인덱스를 못 타는 이유를 짚고, PostgreSQL 표현식 인덱스, MySQL 8 함수 인덱스와 생성 컬럼, SQLite 표현식 인덱스로 다시 인덱스를 태우는 방법과 함정을 정리했다.
인덱스가 있는데도 PostgreSQL이 Seq Scan을 고르는 이유를 비용 공식으로 직접 계산해 본다. 선택도, 테이블 크기, random_page_cost, correlation이 비용을 어떻게 바꾸는지, enable_seqscan=off가 왜 더 느릴 수 있는지 다룬다.
(status, created_at)과 (created_at, status) 인덱스에 같은 쿼리를 걸고 읽은 페이지 수를 비교했다. 범위 조건 뒤 컬럼이 탐색에 쓰이지 못하는 이유를 B-tree 구조로 풀었다.
OFFSET 100000이 실제로 읽고 버리는 행 수를 EXPLAIN ANALYZE로 재 보고, (created_at, id) 행 값 비교와 인덱스 방향을 맞춘 키셋 페이지네이션으로 옮긴 과정과 페이지 번호를 포기한 대가를 기록한다.