C.W.K.
Stream
Lesson 10 of 10 · published

인덱스 전략: 적은 게 종종 더 많음

~14 min · indexes, strategy

Level 0스키마 새싹
0 XP0/86 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

모든 인덱스의 비용

각 인덱스가 INSERT, UPDATE (인덱스 컬럼), DELETE 더 비싸게 만듦. 각 인덱스가 디스크 공간 + 캐시 경쟁. 각 인덱스가 VACUUM 유지보수 필요. 인덱스가 write throughput 도 제약. 측정 없이 '빠르게' 인덱스 추가 = read 쿼리 의미있게 안 빠르게 하면서 write 5× 느리게 만드는 좋은 방법.

세 룰의 전략

  1. 측정한 쿼리에 인덱스 추가 — 상상한 쿼리 아님. EXPLAIN ANALYZE 가 진실.
  2. FK 항상 인덱스 — 부모 행 삭제 일어나는 테이블에 예외 없음.
  3. 주기적으로 미사용 인덱스 정리pg_stat_user_indexes.idx_scan = 0 이 몇 달 = 인덱스 짐만 됨.

미사용 인덱스 감사

PostgreSQL 이 각 인덱스 사용 빈도 추적. 앱이 잠시 프로덕션 있은 후 이 catalog 쿼리가 아무도 안 쓰는 인덱스 드러냄. Sanity check 후 drop.

Code

미사용 인덱스 찾기·sql
SELECT
    s.schemaname || '.' || s.relname AS table,
    s.indexrelname                   AS index,
    pg_size_pretty(pg_relation_size(s.indexrelid)) AS size,
    s.idx_scan                       AS times_used
FROM   pg_stat_user_indexes s
JOIN   pg_index i ON i.indexrelid = s.indexrelid
WHERE  NOT i.indisunique           -- unique 인덱스 절대 drop 안 함
ORDER  BY s.idx_scan, pg_relation_size(s.indexrelid) DESC;
-- 긴 기간 idx_scan = 0 인덱스가 drop 후보.
중복 인덱스 찾기·sql
-- 같은 컬럼 덮는 두 인덱스 = 순수 오버헤드
SELECT
    indrelid::regclass AS table,
    array_agg(indexrelid::regclass) AS indexes,
    array_agg(indkey) AS column_lists
FROM   pg_index
GROUP  BY indrelid, indkey
HAVING COUNT(*) > 1;
Bloat 위한 reindex (드물지만 유용)·sql
-- 무거운 churn 후 인덱스 bloat 가능. REINDEX 가 재구축.
REINDEX INDEX CONCURRENTLY orders_customer_idx;
-- CONCURRENTLY 가 긴 lock 회피; 프로덕션에 사용.

External links

Exercise

실제 DB 에 미사용 인덱스 쿼리 실행. drop 후보 셋 식별. 코드 검색으로 아무것도 참조 안 하는 거 확인. 트랜잭션에서 drop; 뭐 깨지면 ROLLBACK.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.