예상과 달랐던 PostgreSQL 인덱스 사용 — 왜 인덱스를 타지 않았을까?
PostgreSQL 인덱스가 의도대로 동작하지 않았던 이유에 관한 포스팅입니다.
느려진 쿼리, 의외의 원인
최근 운영 중인 시스템에서 Datadog APM을 통해 슬로우 쿼리를 발견했다. 아래처럼 간단한 조건과 LIMIT 1만 있는 쿼리였다.
SELECT *
FROM user_wallets
WHERE network_type = 'mainnet'
AND account_type = 'user'
AND is_blocked = false
AND chain = 'BTC'
AND is_active = true
LIMIT 1
하지만 EXPLAIN ANALYZE 결과를 보니, 예상과 달리 Index Scan이 아닌 Seq Scan(전체 테이블 스캔) 이 발생하고 있었다.
-> Seq Scan on user_wallets
Filter: ((NOT is_blocked) AND is_active AND ...)
Rows Removed by Filter: 4,247,359
Execution Time: 958ms
쿼리 결과는 단 1건만 필요했지만, 전체 400만 건이 넘는 데이터를 스캔하면서 조건에 맞는 결과를 찾고 있었던 것이다.
❓ 인덱스가 왜 안 타졌을까?
해당 테이블에는 아래와 같은 인덱스가 존재하고 있었다.
CREATE INDEX idx_chain_active_wallets
ON user_wallets (chain, is_active)
WHERE network_type = 'mainnet'
AND account_type = 'user'
AND is_blocked = false;
조건도 잘 맞고, LIMIT 1도 붙어 있었기 때문에 인덱스를 사용할 줄 알았지만... PostgreSQL은 인덱스를 사용하지 않았다.
그 이유는 바로:
"정렬 기준이 명확하지 않았기 때문"이다.
✅ 해결 방법: ORDER BY로 정렬 기준을 알려주자
PostgreSQL 입장에서 LIMIT 1이 있다면 "어떤 순서로 정렬해서 가장 앞의 1건을 가져올지" 를 알아야 한다.
그렇지 않으면 전체를 다 스캔한 뒤 조건에 맞는 1건을 찾아낼 수밖에 없다.
따라서 아래와 같이 ORDER BY를 명시해 정렬 기준을 알려주면 인덱스를 활용할 수 있게 된다.
SELECT *
FROM user_wallets
WHERE network_type = 'mainnet'
AND account_type = 'user'
AND is_blocked = false
AND chain = 'BTC'
AND is_active = true
ORDER BY is_active DESC
LIMIT 1;
결과:
-> Index Scan Backward using idx_chain_active_wallets on user_wallets
Index Cond: ((chain = 'BTC') AND (is_active = true))
Execution Time: 0.042ms
불과 1초 가까이 걸리던 쿼리가 0.04ms로 개선됐다.
🔁 ASC vs DESC: 정렬 방향이 성능에 영향을 미친 이유
BOOLEAN 타입은 PostgreSQL에서 기본적으로 false → true 순서(ASC)로 정렬된다.
예를 들어,
ORDER BY is_active ASC: false부터 시작해서 true를 찾음ORDER BY is_active DESC: true부터 시작해서 첫 번째 true를 바로 찾음
위 쿼리에서는 is_active = true 조건을 포함하고 있기 때문에, DESC 정렬이 훨씬 유리했다.
요약하면:
| 정렬 방식 | 인덱스 탐색 방향 | 결과를 언제 찾는가 |
|---|---|---|
| ASC | Forward Scan | false부터 → true까지 가야 함 |
| DESC | Backward Scan | true부터 바로 찾음 |
마무리하며
LIMIT 1이 있는 단순 조회 쿼리에서도, 인덱스를 제대로 타지 않으면 성능이 수백 배 이상 느려질 수 있다.
이번 케이스를 통해 얻은 교훈은 아래와 같다.
✅ 꼭 점검하자!
- 인덱스가 있어도 정렬 기준이 없으면 전체 스캔이 일어날 수 있다.
ORDER BY를 추가해서 정렬 방향을 명확히 지정해주자.BOOLEAN컬럼은false → true로 정렬되며, true를 빠르게 찾고 싶다면 DESC를 써야 한다.
이런 식으로 사소해 보이는 쿼리도 인덱스 사용 방식과 정렬 방향에 따라 성능 차이가 엄청날 수 있다.
Datadog 같은 APM 도구로 슬로우 쿼리를 발견하고 원인을 분석해보는 습관은 꼭 들여두자.