인덱스가 해가 되는 경우: 쓰기와 선택도
쓰기 증폭과 선택도가 낮은 열에 만든 인덱스가 쓸모없는 이유를 알아봅니다.
인덱스가 해가 되는 경우: 쓰기와 선택도은(는) CoddyKit의 무료 Coding Interview Prep 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Coding Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
질문 속 진짜 질문
인덱스가 도움이 되는 이유를 세 번에 걸쳐 배운 뒤, 면접관은 질문을 뒤집습니다. ‘왜 모든 열에 그냥 인덱스를 만들지 않나요?’ 뛰어난 지원자는 인덱스에 실제 비용이 있으며, 쓰기와 캐시 및 저장 공간에 부담을 주고, 플래너가 전혀 사용하지 않을 인덱스도 있다는 점을 설명합니다.
이 강의에서는 인덱스가 성능을 해칠 수 있는 두 가지 큰 이유인 쓰기 증폭과 낮은 선택도를 다룹니다.
인덱스 하나마다 쓰기가 느려집니다
인덱스는 테이블과 동기화된 상태를 유지해야 합니다. 모든 INSERT, 모든 DELETE, 그리고 인덱싱된 열에 대한 모든 UPDATE는 인덱스 구조도 업데이트해야 합니다. 이것이 쓰기 증폭입니다. 즉, 한 행의 변경이 테이블 쓰기 하나와 영향을 받는 인덱스마다 쓰기 하나로 늘어납니다.
인덱스가 8개인 테이블은 인덱스가 없는 테이블보다 쓰기 작업을 대략 9배 부담합니다. 쓰기가 많거나 처리량이 높은 테이블에서는 상당히 큰 비용입니다.
예제로 살펴보기: 쓰기 비용
초당 수천 개의 행을 수집하는 이벤트 테이블을 생각해 보세요. 추가 인덱스가 하나 늘어날 때마다 삽입 작업이 더 많은 일을 수행하고, 인덱스 페이지를 분할하며, 리프를 업데이트하고, 캐시를 두고 경쟁합니다.
추가 전용이고 쓰기 중심인 테이블에서는 기본 키 외에 인덱스를 거의 또는 전혀 만들지 않는 것이 올바른 답인 경우가 많습니다. 대신 복제본이나 데이터 웨어하우스에서 대량의 읽기 작업을 수행합니다.
-- Each of these indexes adds cost to EVERY insert below
CREATE INDEX ix_events_user ON events (user_id);
CREATE INDEX ix_events_type ON events (event_type);
CREATE INDEX ix_events_ts ON events (created_at);
INSERT INTO events (user_id, event_type, created_at)
VALUES (42, 'click', now()); -- now updates table + 3 indexes선택도의 의미
선택도는 열이 행을 얼마나 잘 구분하는지를 나타내며, 일반적인 값 하나가 일치시키는 행의 비율입니다. 선택도가 높으면 값 하나에 해당하는 행이 적습니다(이메일이나 UUID처럼). 선택도가 낮으면 값 하나에 해당하는 행이 많습니다(부울 값이나 세 가지 옵션만 있는 상태처럼).
인덱스는 거의 모든 행을 제외할 수 있는 선택도가 높은 열에서 효과가 큽니다. 선택도가 낮은 열에서는 그렇지 않은 경우가 많습니다.
선택도가 낮은 인덱스가 쓸모없는 이유
is_active가 사용자 중 90%에서 true라고 가정해 보겠습니다. 인덱스 조회는 테이블의 90%를 반환하게 되며, 이 정도로 많은 행에 대해서는 엔진이 행마다 힙 페치를 수행해야 합니다. 이는 테이블을 한 번에 순차적으로 스캔하는 것보다 느립니다.
따라서 플래너는 인덱스를 올바르게 무시하고 순차 스캔을 수행합니다. 그러면 인덱스는 읽기 성능상의 이점은 전혀 주지 않으면서 쓰기 추가 비용과 저장 공간만 차지합니다.
-- 90% of rows match: the planner will likely skip this index
CREATE INDEX ix_users_active ON users (is_active);
SELECT * FROM users WHERE is_active = true;대략적인 기준
말로 설명하기 좋은 기준은 다음과 같습니다. 조건식이 테이블의 대략 5~20% 이상과 일치하면 일반적으로 순차 스캔이 인덱스 스캔보다 빠릅니다. 무작위 힙 페치가 페이지를 순서대로 스트리밍하는 것보다 비용이 많이 들기 때문입니다.
정확한 경계는 행 크기, 캐싱 및 저장 장치 속도에 따라 달라집니다. 그래서 플래너는 고정된 숫자가 아니라 통계를 사용해 결정합니다.
부분 인덱스가 해결책입니다
값의 분포가 치우친 열에서 드문 값만 조회한다면 부분 인덱스(PostgreSQL)를 사용해 해당 행만 인덱싱할 수 있습니다. 작고 선택도가 높으며 유지 비용도 저렴합니다.
주문 중 1%가 pending이고 그 주문을 계속 조회한다면 해당 주문만 인덱싱하세요. 인덱스가 작게 유지되므로 플래너도 기꺼이 사용합니다.
-- Index only the rare, frequently-queried rows
CREATE INDEX ix_orders_pending
ON orders (created_at)
WHERE status = 'pending';오래된 통계가 플래너를 잘못 이끕니다
최적화기는 열 통계를 바탕으로 인덱스와 스캔 중에서 선택합니다. 대량 적재나 대규모 업데이트 후 통계가 오래된 상태라면 선택도를 잘못 판단해 올바르지 않은 실행 계획을 선택할 수 있습니다.
면접관이 ‘인덱스가 있는데 사용되지 않습니다’라고 말할 때는 인덱스 자체를 탓하기 전에 ANALYZE로 통계를 갱신하겠다는 답변을 포함하는 것이 좋습니다.
ANALYZE orders; -- refresh planner statistics인덱스가 성능을 해치는 다른 방식
잘 알려지지 않은 비용도 언급해 답변을 완성하세요.
- 저장 공간과 캐시: 인덱스가 디스크를 차지하고 메모리를 두고 경쟁하므로 유용한 데이터 페이지가 밀려날 수 있습니다.
- 중복되거나 겹치는 인덱스: 유지 관리는 되지만 한 번도 선택되지 않습니다.
- 비대화: 대규모 업데이트가 발생하면 B-트리가 조각화되어
REINDEX가 필요합니다. - 최적화기 혼란: 유사한 인덱스가 너무 많으면 계획 수립이 느려지고 예측하기 어려워집니다.
사용되지 않는 인덱스 찾기
실제 환경에서 정리를 정당화하려면 PostgreSQL이 인덱스 사용량을 추적한다는 점을 언급하세요. idx_scan = 0인 인덱스는 제거 후보입니다. 읽기에 한 번도 사용되지 않으면서 쓰기와 저장 공간 비용만 발생시키기 때문입니다.
SELECT relname AS table_name, indexrelname AS index_name, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY relname;면접에서 이렇게 설명하세요
완전하고 균형 잡힌 요약은 다음과 같습니다.
'인덱스에는 쓰기 증폭 비용이 있습니다. 삽입, 갱신, 삭제가 발생할 때마다 인덱스를 유지해야 하고, 저장 공간과 캐시에도 부담을 줍니다. 인덱스는 선택도가 높은 조건식에서만 효과가 있습니다. 대부분의 행이 일치하는 열에서는 플래너가 순차 스캔을 선택하는 것이 옳으므로 인덱스는 순수한 추가 비용입니다. 값의 분포가 치우친 열에는 부분 인덱스를 활용하고, ANALYZE로 통계를 최신 상태로 유지하며 사용하지 않는 인덱스는 제거합니다.'
빠른 확인
비용에 비해 가치가 가장 낮을 가능성이 큰 인덱스를 판단해 보세요.
복습: 인덱스가 성능을 해치는 경우
핵심 요점:
- 모든 인덱스는 쓰기 증폭과 저장 공간 및 캐시 비용을 추가합니다.
- 인덱스는 선택도가 높은 열에서 효과가 있습니다. 선택도가 낮은 열에서는 플래너가 순차 스캔을 선호합니다.
- 일치하는 행이 대략 5~20%를 넘으면 일반적으로 스캔이 더 빠릅니다.
- 드문 값만 조회하는, 값의 분포가 치우친 열에는 부분 인덱스를 사용하세요.
ANALYZE로 통계를 최신 상태로 유지하고 사용하지 않는 인덱스(idx_scan = 0)를 제거하세요.
이로써 인덱스 전략 과정이 끝났습니다. 인덱스가 가치를 발휘하는 곳에 만들고 실행 계획으로 그 효과를 입증하세요.
자주 묻는 질문
“인덱스가 해가 되는 경우: 쓰기와 선택도” 강의는 무료인가요?
네 — “인덱스가 해가 되는 경우: 쓰기와 선택도” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Coding Interview Prep 강의 전체를 잠금 해제할 수 있습니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
“인덱스가 해가 되는 경우: 쓰기와 선택도”에서 뭘 배우나요?
쓰기 증폭과 선택도가 낮은 열에 만든 인덱스가 쓸모없는 이유를 알아봅니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Coding Interview Prep을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Coding Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“인덱스가 해가 되는 경우: 쓰기와 선택도” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Coding Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Coding Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- B-트리 인덱스와 그 효과
- 복합 인덱스 열 순서
- 커버링 인덱스와 인덱스 전용 스캔
- 인덱스가 해가 되는 경우: 쓰기와 선택도