인덱스는 공짜가 아니다( 인덱스의 단점)
저장 공간 (Storage)
- 인덱스는 원본 테이블과는 별개로, B-Tree 구조를 가진 물리적인 파일로 디스크에 저장된다.
- 즉, 인덱스를 생성하면 그만큼의 추가 저장 공간이 필요하다.
- 인덱스는 어떻게 구성하는지에 따라 다르지만, 일반적으로 원본 테이블 크기의 약 10% 내외의 공간을 추가로 차지한다고 알려져 있다.
- 만약 100GB에 달하는 거대한 items 테이블이 있고, 여기에 5개의 인덱스를 추가로 생성한다면?
- 인덱스만으로 약 50GB라는 무시할 수 없는 추가 디스크 공간이 필요하게 된다. 인덱스를 무분별하게 생성하면 디스크 사용량이 계속해서 늘어나는 것을 보게 될 것이다.
쓰기 성능 (INSERT, UPDATE, DELETE)
인덱스는 "SELECT"의 속도를 높이는 대가로, "INSERT", "UPDATE", "DELETE"의 속도를 희생시킨다. 데이터에 변경이 일어날 때마다, 데이터베이스는 원본 테이블뿐만 아니라 이와 관련된 모든 인덱스를 함께 수정해야 하기 때문이다
- INSERT: 새로운 상품이 등록되면(INSERT), items 테이블에 행이 추가된다. 동시에, 이 테이블에 생성된 모든 인덱스(예: PRIMARY, seller_id, idx_items_category_price)의 B-Tree에도 새로운 데이터에 대한 키 값과 주소가 추가되어야 한다.
- 이 과정에서 B-Tree의 정렬 순서를 유지하고 균형을 맞추기 위한 추가적인 연산이 발생한다.
- 인덱스가 5개라면, 테이블 삽입 1번에 인덱스 삽입 5번의 작업이 추가되는 셈이다.
- DELETE: 상품이 삭제되면(DELETE), 테이블에서 행이 사라진다.
- 동시에 모든 인덱스에서도 해당 상품에 대한 키 값이 삭제되어야 한다.
- UPDATE: 상품 정보가 수정될 때가 가장 복잡하다.
- 만약 인덱스가 없는 "stock_quantity" 컬럼의 값이 변경된다면, 인덱스는 수정할 필요가 없으므로 비교적 빠르다.
- 하지만 인덱스가 있는 "price" 컬럼의 값이 변경된다면, 데이터베이스는 기존 "price" 값으로 된 인덱스 항목을 "삭제"하고, 새로운 "price" 값으로 인덱스 항목을 '추가'하는 것과 유사한 작업을 수행한다.
- 왜냐하면 인덱스는 변경된 값에 맞추어 새로운 정렬 상태를 유지해야 하기 때문이다.
- 이는 "INSERT"와 "DELETE"가 동시에 발생하는 것과 같아 부하가 크다.
실무 가이드: 균형의 미학
워크로드를 분석하라: 읽기 vs 쓰기
- 읽기 중심(Read-heavy) 서비스: 데이터 분석 시스템, 블로그, 뉴스 사이트처럼 데이터 변경보다는 조회가 훨씬 더 빈번한 서비스라면, 다양한 조회 성능을 높이기 위해 인덱스를 비교적 자유롭게 생성해도 좋다.
- 우리 쇼핑몰의 상품 조회 기능이 대표적이다.
- 쓰기 중심(Write-heavy) 서비스: 실시간으로 데이터를 기록하는 로깅 시스템, 주식 거래 시스템, 채팅 서비스처럼 "INSERT" 나 "UPDATE"가 매우 빈번한 서비스라면, 인덱스 생성에 매우 신중하고 보수적이어야 한다.
- 모든 인덱스는 쓰기 작업에 오버헤드를 추가하기 때문이다.
- 꼭 필요한 최소한의 인덱스만 유지해야 한다.
"혹시나 해서" 인덱스를 만들지 마라.
사용하지 않는 인덱스는 저장 공간만 차지하고 쓰기 성능만 저하시키는 암적인 존재다. 명확한 목적 없이, "나중에 쓸 것 같아서"라는 이유로 인덱스를 미리 만드는 것은 좋지 않다. 느린 쿼리가 발견되었을 때, 그 쿼리를 개선하기 위한 목적으로 생성해야 한다.
사용하지 않는 인덱스는 주기적으로 정리하라.
대부분의 데이터베이스는 특정 인덱스가 얼마나 사용되었는지 모니터링하는 기능을 제공한다. 몇 달, 혹은 1년 이상 아무도 사용하지 않는 인덱스가 있다면, 과감하게 삭제하여 시스템 자원을 확보하고 쓰기 성능을 높여야 한다.
인덱스 컬럼은 가공하면 안 된다.
WHERE 절에서 인덱스가 적용된 컬럼을 함수로 감싸거나 계산을 하는 등 가공하게 되면 인덱스가 적용되지 않는다. 이는 실무에서 정말 자주 하는 실수이므로 반드시 기억해야 한다.
예를 들어 WHERE 절에서 인덱스가 적용된 컬럼에 "SUBSTRING()" 같은 함수를 사용하거나 연산을 하면 인덱스가 작동하지 않아 테이블 전체를 스캔하게 되므로 성능이 크게 저하된다.
문제: "WHERE SUBSTRING(item_name, 1, 5) = '게이밍'"처럼 인덱스 컬럼(item_name)을 가공하면, 데이터베이스는 정렬된 인덱스를 활용하지 못하고 모든 데이터를 일일이 확인한다.
- "WHERE indexed_column * 10 = 100" 이런 경우도 마찬가지로 인덱스를 사용하지 못한다.
원인: 인덱스는 가공되지 않은 원본 값을 기준으로 만들어지기 때문이다.
해결책: 컬럼 자체를 가공하는 대신, LIKE 연산자를 사용하여 "WHERE item_name LIKE '게이밍%'"와 같이 조건을 변경해야 인덱스를 효율적으로 사용할 수 있다.
결론: SQL 성능을 높이려면 인덱스 컬럼은 절대 가공하지 말고 원본 상태 그대로 사용해야 한다
'데이터 베이스 > 데이터베이스 기본' 카테고리의 다른 글
| Ch07. 인덱스 - 복합 인덱스(3) (0) | 2026.06.07 |
|---|---|
| Ch07. 인덱스 - 복합 인덱스(2) (0) | 2026.06.07 |
| Ch07. 인덱스 - 인덱스 설계 가이드라인 (0) | 2026.06.06 |
| Ch07. 인덱스 - 복합 인덱스(1) (0) | 2026.06.06 |
| Ch07. 인덱스 - 커버링 인덱스 (0) | 2026.06.06 |