예상과 달랐던 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 도구로 슬로우 쿼리를 발견하고 원인을 분석해보는 습관은 꼭 들여두자.