2026년 1월 1일
[커넥트 지누] 학식 조회 성능 개선
인덱스 조회
SELECT * FROM pg_indexes WHERE tablename = 'cafeteria_diet';
실행 결과
indexname | indexdef |
cafeteria-diet_pkey | CREATE UNIQUE INDEX "cafeteria-diet_pkey" ON public.cafeteria_diet USING btree (diet_id) |
인사이트
- PostgreSQL은 MySQL과 다르게 FK 제약조건 생성 시 자동으로 인덱스를 만들어주지 않는다.
최적화 대상 쿼리문
SELECT dish_category, dish_type, dish_name FROM cafeteria_diet WHERE cafeteria_id = 1 AND date = '2025-06-30' AND time = '아침';
실행 결과
dish_category | dish_type | dish_name |
한식 | 주식 | 기장밥 |
한식 | 국류 | 해물보탕국 |
한식 | 찬류 | 치즈불닭 |
1. 인덱스가 없을 경우
EXPLAIN ANALYZE SELECT dish_category, dish_type, dish_name FROM cafeteria_diet WHERE cafeteria_id = 4 AND date = '2025-05-13' AND time = '점심';
실행 결과
QUERY PLAN |
Seq Scan on cafeteria_diet (cost=0.00..1007.70 rows=3 width=39) (actual time=1.263..3.327 rows=9 loops=1) |
Filter: ((cafeteria_id = 4) AND (date = '2025-05-13'::date) AND (("time")::text = '점심'::text)) |
Rows Removed by Filter: 32831 |
Planning Time: 0.068 ms |
Execution Time: 3.350 ms |
2. FK 단일 인덱스
FK 단일 인덱스 추가
CREATE INDEX idx_cafeteria_diet_cafeteria_id ON cafeteria_diet (cafeteria_id);
실행 결과
QUERY PLAN |
Bitmap Heap Scan on cafeteria_diet (cost=20.86..494.14 rows=3 width=39) (actual time=0.260..0.425 rows=9 loops=1) |
Recheck Cond: (cafeteria_id = 4) |
Filter: ((date = '2025-05-13'::date) AND (("time")::text = '점심'::text)) |
Rows Removed by Filter: 2279 |
Heap Blocks: exact=84 |
-> Bitmap Index Scan on idx_cafeteria_diet_cafeteria_id (cost=0.00..20.86 rows=2302 width=0) (actual time=0.089..0.089 rows=2288 loops=1) |
Index Cond: (cafeteria_id = 4) |
Planning Time: 0.083 ms |
Execution Time: 0.447 ms |
3. 커버링 인덱스를 사용할 경우
커버링 인덱스 추가
CREATE INDEX idx_cover ON cafeteria_diet (date, cafeteria_id, time) INCLUDE (dish_category, dish_type, dish_name);
실행 결과
QUERY PLAN |
Index Only Scan using idx_cafeteria_diet_cover on cafeteria_diet (cost=0.41..2.68 rows=3 width=39) (actual time=0.019..0.021 rows=9 loops=1) |
Index Cond: ((date = '2025-05-13'::date) AND (cafeteria_id = 4) AND ("time" = '점심'::text)) |
Heap Fetches: 0 |
Planning Time: 0.083 ms |
Execution Time: 0.041 ms |
실험 결과
인덱스 구성 | 실행 계획 | 실행 시간 | 인덱스 크기 (PK 제외) | 특징 |
1️⃣ 인덱스 없음 | Seq Scan | 3.350 ms | - | 테이블 풀 스캔 |
2️⃣ FK 단일 인덱스 | Bitmap Heap Scan | 0.447 ms | 240 kB | FK 기준 필터링 |
3️⃣ 커버링 인덱스 | Index Only Scan | 0.041 ms | 2656 kB | 인덱스만으로 처리 |
얻을 수 있는 인사이트
- FK 인덱스 추가만으로도 약 7.5배 성능 개선됐다. (3.350 ms → 0.447 ms)
- 커버링 인덱스로 Heap 접근을 제거하면 추가로 약 11배 개선된다. (0.447 ms → 0.041 ms)
- 결과적으로 디스크 I/O를 거의 제거하여 전체 쿼리 성능이 약 82배 향상된다.
- 단, 인덱스 크기는 성능 개선에 비례해 증가한다.
- 따라서 커버링 인덱스는 조회가 많고, 쿼리 패턴이 거의 고정된 경우에만 사용하는 것이 적절하다.
- 조회 컬럼이 자주 바뀌거나, 쓰기가 많은 테이블에서는 오히려 인덱스 유지 비용이 커질 수 있으므로 신중하게 선택해야 한다.
