인덱스의 비용과 함정 —
많다고 모두 빨라지지 않는다
도서관에 제목별 찾아보기가 있으면 책 찾기는 아주 편합니다. 하지만 새 책이 들어오면 책장에만 꽂는 게 아니라 찾아보기에도 위치를 적어야 하죠. 인덱스도 똑같습니다 — 빠르게 찾도록 도와주지만, 데이터가 바뀔 때마다 함께 관리해야 합니다.
이번 섹션의 여정
완료 ✓
완료 ✓
완료 ✓
지금 여기
인덱스에는 두 가지 비용이 있습니다
빠른 조회찾을 위치로 바로 이동한다
정렬 절약이미 정리돼 있어 줄을 다시 세우지 않는다
저장 공간원본 데이터 외에 인덱스를 담을 공간이 더 필요하다
쓰기 부담INSERT · UPDATE · DELETE 때 관련 인덱스도 함께 바뀐다
검색을 도와주는 안내판이 많아질수록 — 새 데이터를 넣거나 고칠 때 손볼 안내판도 함께 늘어납니다.
데이터가 적을 때는 차이가 잘 안 느껴집니다. 우리 shop처럼 상품 30개, 주문 40건이면 인덱스를 몇 개 만들든 체감이 없어요. 하지만 데이터가 아주 많고 인덱스도 많아지면 필요한 공간과 쓰기 시간이 함께 커집니다.
많이 만들면 생기는 문제
제목 찾아보기가 이미 있는데 이름만 다른 똑같은 찾아보기를 또 만든다면 어떨까요? 찾는 데 도움은 거의 늘지 않지만, 보관하고 고칠 대상만 늘어납니다.
같은 역할을 하는 인덱스가 이미 있는지 먼저 확인하세요. 6-2에서 봤듯
orders.member_id 처럼 외래키 인덱스가 이미 깔려 있는 컬럼도 있고, 6-3에서
봤듯 (member_id, ordered_at) 복합 인덱스가 있으면 member_id 단독 인덱스는
따로 필요 없습니다.
함정 1 — 컬럼을 가공해서 검색하기
첫 번째 함정은 인덱스가 걸린 값을 다른 모양으로 바꿔서 검색하는 경우입니다.
주문일에서 연도만 꺼내 2026년 주문을 찾아볼게요.
결과는 맞게 나옵니다. 2026년 주문 11건이 정확히 나와요. 하지만 원래 날짜를 함수로 한 번 바꿨기 때문에, 날짜순으로 정리된 인덱스에서 시작 위치를 바로 찾기 어렵습니다.
인덱스는 ordered_at 값 자체를 기준으로 정리돼 있는데, 우리가 찾는 건 YEAR(ordered_at)이라는 가공된 값이니까요. 정리 기준과 찾는 기준이 어긋난 겁니다.
이번에는 날짜를 바꾸지 않고 범위로 표현해 보겠습니다.
| 조건 쓰는 법 | 인덱스에서 시작점을 찾을 수 있나 |
|---|---|
| YEAR(ordered_at) = 2026 | 어렵다 — 정리 기준과 찾는 기준이 다르다 |
| ordered_at >= '2026-01-01' AND < '2027-01-01' | 쉽다 — 시작 날짜로 바로 이동한다 |
6-3에서 만든 건 (member_id, ordered_at) 복합 인덱스라, ordered_at
단독으로는 쓰기 어렵습니다 — 맨 왼쪽 컬럼이 아니니까요(6-3의 왼쪽 우선
규칙). 이 함정을 직접 확인하려면 CREATE INDEX idx_orders_date ON orders (ordered_at); 로 단독 인덱스를 하나 만들고 두 쿼리에 EXPLAIN 을 붙여
비교해 보세요.
함정 2 — 첫 글자를 모르는 검색
두 번째 함정은 LIKE 검색에서 첫 글자를 모르는 경우입니다.
6-1에서 products.product_name에 인덱스를 만들었죠. 그 인덱스는 상품 이름이 가나다순으로 정리돼 있습니다.
| 조건 | 찾는 상품 | 인덱스를 쓸 수 있나 |
|---|---|---|
| product_name LIKE '면%' | 면 셔츠 · 면바지 | ○ '면' 으로 시작하는 위치로 이동 |
| product_name LIKE '%셔츠' | 라운드 티셔츠 · 면 셔츠 | ✕ 어디서부터 볼지 정할 수 없다 |
'면%'은 첫 글자를 알고 있으니 가나다순 목록에서 면으로 시작하는 자리로 바로 갈 수 있습니다. 하지만 '%셔츠'는 앞에 무엇이 오는지 모릅니다 — 라운드 티셔츠일 수도 있고 면 셔츠일 수도 있죠. 시작점을 정할 수 없으니 일반적인 인덱스로는 넓게 살펴보게 됩니다.
인덱스는 정렬된 목록입니다. 정렬된 목록이 힘을 쓰려면 어디서부터 볼지 정할 수 있어야 해요. 컬럼을 가공하거나 앞부분을 모르면 — 그 시작점을 잃습니다.
인덱스를 만들기 전 세 가지 질문
이 세 가지에 답하면 필요 없는 인덱스를 줄이고, 꼭 필요한 인덱스를 고르기 쉬워집니다.
좋은 인덱스는 많이 만든 인덱스가 아니라, 자주 사용하는 조회에 꼭 필요한 인덱스입니다.
한눈에 정리
| 주제 | 핵심 |
|---|---|
| 비용 ① | 저장 공간 — 원본 외에 인덱스를 담을 자리 |
| 비용 ② | 쓰기 부담 — INSERT · UPDATE · DELETE 때 함께 갱신 |
| 함정 ① | 컬럼을 가공하면(YEAR(...)) 시작점을 잃는다 |
| 함정 ② | 앞부분을 모르는 LIKE '%...' 는 시작점이 없다 |
| 판단 기준 | 조회 이익과 저장 · 쓰기 · 관리 비용의 균형 |
여기까지가 섹션 6입니다. 데이터를 빠르게 찾는 구조를 봤어요 — 인덱스가 무엇인지, 어디에 만들지, 컬럼 순서를 어떻게 정할지, EXPLAIN으로 확인하는 법, 그리고 오늘의 비용과 함정까지.
다음 섹션에서는 여러 SQL을 하나의 업무로 묶는 트랜잭션을 배웁니다.