데이터베이스 인덱싱 최적화 기법: 성능을 좌우하는 핵심 기술
웹 애플리케이션에서 데이터베이스는 핵심적인 역할을 수행합니다. 수천 건, 수백만 건의 데이터를 효율적으로 검색하고 처리하려면 쿼리 성능을 최적화해야 하며, 그 중심에는 바로 **인덱싱(Indexing)**이 있습니다.
하지만 단순히 인덱스를 많이 만든다고 해서 성능이 무조건 좋아지는 것은 아닙니다.
오히려 잘못된 인덱스 설계는 디스크 공간 낭비, 느려진 INSERT/UPDATE/DELETE 성능 등의 문제를 초래할 수 있습니다.
이 글에서는 인덱스의 기본 개념부터 실무 최적화 기법까지 자세히 살펴보겠습니다.
1. 인덱스란 무엇인가?
인덱스는 책의 목차와 비슷한 구조입니다. 데이터베이스는 인덱스를 통해 특정 데이터를 빠르게 찾을 수 있으며, **풀 테이블 스캔(Full Table Scan)**을 줄여줍니다.
- Without Index: 테이블 전체를 훑음 → 느림
- With Index: 특정 위치로 바로 접근 → 빠름
기본적으로 대부분의 RDBMS(MySQL, PostgreSQL 등)는 B-tree 인덱스를 사용하며, 데이터가 정렬된 구조로 저장되어 있습니다.
2. 인덱스의 종류
1) 기본 인덱스 (Single-column Index)
- 한 개의 컬럼에 인덱스를 생성
- 예: CREATE INDEX idx_name ON users(name);
2) 복합 인덱스 (Composite Index)
- 두 개 이상의 컬럼에 인덱스를 생성
- 예: CREATE INDEX idx_name_email ON users(name, email);
- 주의: 컬럼 순서 중요. 앞에서부터 차례로 필터링해야 효과 있음
3) 유니크 인덱스 (Unique Index)
- 중복을 허용하지 않는 인덱스
- 예: CREATE UNIQUE INDEX idx_email ON users(email);
4) 함수 기반 인덱스 (Function-based Index)
- 함수 결과에 인덱스를 생성
- 예: CREATE INDEX idx_lower_name ON users(LOWER(name));
5) 풀텍스트 인덱스 (Full-text Index)
- 문장 검색(검색어 포함 여부 등)을 위한 인덱스
- 예: FULLTEXT(name, description) (MySQL)
6) GiST, GIN 인덱스 (PostgreSQL 전용)
- 복잡한 데이터 타입(JSON, 배열 등) 또는 유사도 검색에 사용
3. 인덱스 최적화 전략
1) 쿼리 패턴 분석
인덱스를 만들기 전에, 가장 먼저 해야 할 일은 자주 사용되는 쿼리를 파악하는 것입니다.
- WHERE 조건
- JOIN 조건
- ORDER BY 또는 GROUP BY
Tip: MySQL의 경우 EXPLAIN, PostgreSQL은 EXPLAIN ANALYZE 명령어를 활용하면 인덱스 사용 여부를 확인할 수 있습니다.
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
2) 복합 인덱스에서 컬럼 순서 주의
복합 인덱스는 순서가 성능에 매우 중요합니다.
예를 들어, CREATE INDEX idx_name_email ON users(name, email)일 때는 아래와 같은 쿼리에는 적용되지만:
SELECT * FROM users WHERE name = 'Alice';
SELECT * FROM users WHERE name = 'Alice' AND email = 'alice@example.com';
다음 쿼리에는 인덱스가 적용되지 않습니다:
SELECT * FROM users WHERE email = 'alice@example.com'; -- 비효율적
원칙: 인덱스는 선행 컬럼부터 순차적으로 사용할 수 있습니다.
3) 덜 자주 변경되는 컬럼에 인덱스 적용
- 인덱스는 쓰기 성능을 저하시킬 수 있음
- UPDATE, INSERT, DELETE 시 인덱스도 함께 변경됨
- 따라서 변경이 적은 컬럼에 인덱스를 걸어야 효율적
4) SELECTivity 높은 컬럼에 인덱스 생성
- SELECTivity란 특정 조건이 전체 행에서 얼마나 적게 매칭되는지를 의미
- 예: 전체 1,000건 중 3건만 해당되는 조건 → selectivity 높음
- 반대로, 성별(gender = 'F')처럼 데이터가 절반 이상 매칭된다면 인덱스 효과가 낮음
5) 커버링 인덱스 활용
쿼리에서 사용하는 모든 컬럼이 인덱스에 포함되어 있으면, 인덱스만으로 데이터를 처리할 수 있습니다. 이를 Covering Index라고 부릅니다.
-- 인덱스: idx_name_email ON (name, email)
SELECT email FROM users WHERE name = 'Alice';
-- → 테이블 접근 없이 인덱스만으로 처리 가능
6) 조건부 인덱스 활용 (PostgreSQL, SQL Server)
특정 조건에만 인덱스를 적용하는 기능입니다.
CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;
이렇게 하면 불필요한 인덱스 유지 비용을 줄일 수 있습니다.
4. 인덱스 실전 튜닝 사례
시나리오
orders 테이블에서 최근 30일 동안 주문된 상품 중, 특정 카테고리에 속하는 상품을 조회하는 쿼리:
SELECT * FROM orders
WHERE category_id = 10 AND created_at > NOW() - INTERVAL 30 DAY;
잘못된 인덱스 설계
CREATE INDEX idx_category ON orders(category_id);
- → created_at 조건이 빠져 있어 쿼리 성능 개선 제한적
개선된 인덱스 설계
CREATE INDEX idx_category_date ON orders(category_id, created_at);
- → 복합 인덱스로 두 조건을 모두 커버
- → 최신 쿼리 캐시와 결합 시 성능 대폭 향상
5. 인덱스 상태 진단 도구
- MySQL
- SHOW INDEX FROM table_name
- EXPLAIN FORMAT=JSON
- Performance Schema
- PostgreSQL
- pg_stat_user_indexes
- pg_stat_statements
- EXPLAIN (ANALYZE, BUFFERS)
불필요한 인덱스는 DROP INDEX로 제거하여 유지 비용을 줄여야 합니다.
6. 마무리: 인덱스는 ‘적절하게’ 사용해야 강력해진다
인덱스는 데이터베이스 성능 향상의 핵심 요소입니다.
그러나 무분별한 인덱스 사용은 오히려 쓰기 성능 저하, 디스크 낭비, 복잡한 유지보수로 이어질 수 있습니다.
따라서, 쿼리 패턴을 분석하고, 실행 계획을 기반으로 꼭 필요한 인덱스만 생성하는 것이 가장 현명한 방법입니다.
꾸준한 모니터링과 튜닝을 통해 인덱스를 지속적으로 개선해 나가시길 바랍니다.
'IT개발' 카테고리의 다른 글
NoSQL 데이터 모델링 베스트 프랙티스 (0) | 2025.04.21 |
---|---|
타임 시리즈 DB 활용: InfluxDB vs TimescaleDB (0) | 2025.04.21 |
분산 트랜잭션과 Saga 패턴 실전 예제 (0) | 2025.04.20 |
스트리밍 데이터 처리: Apache Kafka vs RabbitMQ (0) | 2025.04.20 |
대규모 트래픽 처리를 위한 캐시 전략: Redis와 Memcached 비교 및 활용법 (0) | 2025.04.20 |