인덱스의 개념, 종류, 생성/관리 방법과 효과적인 사용 전략을 정리합니다.

인덱스란?

인덱스는 테이블의 데이터를 빠르게 검색하기 위한 자료구조입니다.
책의 목차(색인)와 같은 역할로, 전체 데이터를 스캔하지 않고 원하는 데이터를 빠르게 찾을 수 있습니다.

인덱스 유무에 따른 차이

항목 인덱스 없음 (Full Table Scan) 인덱스 있음 (Index Scan)
검색 방식 테이블 전체를 순차 탐색 B-Tree 구조로 빠른 탐색
시간 복잡도 O(n) O(log n)
100만 건 검색 100만 행 스캔 약 20회 비교로 도달

인덱스 종류

종류 설명 생성 방법
PRIMARY KEY 기본키 인덱스, 자동 생성 PRIMARY KEY (id)
UNIQUE INDEX 중복 불가 인덱스 UNIQUE KEY uk_email (email)
INDEX (일반) 중복 허용 인덱스 INDEX idx_name (name)
FULLTEXT INDEX 전문 검색 인덱스 FULLTEXT INDEX ft_content (content)
COMPOSITE INDEX 복합 인덱스 (다중 컬럼) INDEX idx_dept_sal (dept_id, salary)

인덱스 생성 및 관리

생성

-- 테이블 생성 시
CREATE TABLE products (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    name     VARCHAR(100) NOT NULL,
    category VARCHAR(50),
    price    DECIMAL(10,2),
    INDEX idx_category (category),
    INDEX idx_cat_price (category, price)
);

-- 기존 테이블에 추가
CREATE INDEX idx_name ON products (name);
CREATE UNIQUE INDEX uk_name ON products (name);

-- ALTER TABLE로 추가
ALTER TABLE products ADD INDEX idx_price (price);
ALTER TABLE products ADD FULLTEXT INDEX ft_name (name);

조회

-- 테이블의 인덱스 목록
SHOW INDEX FROM products;

-- 인덱스 사용 여부 확인 (EXPLAIN)
EXPLAIN SELECT * FROM products WHERE category = '전자제품';

삭제

DROP INDEX idx_name ON products;
ALTER TABLE products DROP INDEX idx_price;

EXPLAIN으로 실행 계획 확인

EXPLAIN SELECT * FROM products WHERE category = '전자제품' AND price > 10000;

EXPLAIN 주요 컬럼

컬럼 설명 좋은 값
type 접근 방식 const > eq_ref > ref > range > index > ALL
possible_keys 사용 가능한 인덱스 인덱스 이름
key 실제 사용된 인덱스 인덱스 이름 (NULL이면 미사용)
rows 예상 스캔 행 수 작을수록 좋음
Extra 추가 정보 Using index (커버링 인덱스)

type 값 상세

type 설명 성능
const PK/UNIQUE로 1건 조회 최고
eq_ref JOIN에서 PK/UNIQUE 매칭 매우 좋음
ref 일반 인덱스 매칭 좋음
range 인덱스 범위 스캔 보통
index 인덱스 전체 스캔 나쁨
ALL 테이블 전체 스캔 최악

복합 인덱스와 최좌선 원칙

복합 인덱스는 컬럼 순서가 매우 중요합니다.
INDEX idx_abc (a, b, c) 인덱스가 있을 때:

WHERE 조건 인덱스 사용 여부
WHERE a = 1 ✅ 사용
WHERE a = 1 AND b = 2 ✅ 사용
WHERE a = 1 AND b = 2 AND c = 3 ✅ 사용 (전체)
WHERE b = 2 ❌ 미사용
WHERE b = 2 AND c = 3 ❌ 미사용
WHERE a = 1 AND c = 3 ⚠️ a만 사용

핵심: 복합 인덱스는 왼쪽 컬럼부터 순서대로 사용됩니다 (Leftmost Prefix Rule).

인덱스가 사용되지 않는 경우

-- ❌ 컬럼에 함수/연산 적용
SELECT * FROM employees WHERE YEAR(hire_date) = 2024;
-- ✅ 범위 조건으로 변경
SELECT * FROM employees WHERE hire_date >= '2024-01-01' AND hire_date < '2025-01-01';

-- ❌ LIKE 앞쪽 와일드카드
SELECT * FROM products WHERE name LIKE '%노트북';
-- ✅ 앞쪽 고정
SELECT * FROM products WHERE name LIKE '삼성%';

-- ❌ 묵시적 타입 변환
SELECT * FROM products WHERE price = '10000';  -- price가 DECIMAL인데 문자열 비교
-- ✅ 올바른 타입 사용
SELECT * FROM products WHERE price = 10000;

-- ❌ OR 조건 (각 컬럼에 개별 인덱스 필요)
SELECT * FROM products WHERE category = 'A' OR price > 10000;

-- ❌ NOT, != , <> 조건
SELECT * FROM products WHERE category != '전자제품';

인덱스 설계 가이드라인

원칙 설명
WHERE 절에 자주 사용되는 컬럼 검색 조건에 자주 등장하는 컬럼에 인덱스 생성
카디널리티가 높은 컬럼 우선 고유 값이 많은 컬럼(이메일, ID)이 효과적
JOIN 조건 컬럼 FK 등 JOIN에 사용되는 컬럼에 인덱스 필수
ORDER BY / GROUP BY 컬럼 정렬/그룹핑에 사용되는 컬럼
과도한 인덱스 지양 INSERT/UPDATE/DELETE 성능 저하 원인
커버링 인덱스 활용 SELECT 컬럼까지 인덱스에 포함하면 테이블 접근 불필요
-- 커버링 인덱스 예시
CREATE INDEX idx_cover ON employees (dept_id, salary, name);

-- 이 쿼리는 테이블에 접근하지 않고 인덱스만으로 처리 (Using index)
SELECT dept_id, salary, name FROM employees WHERE dept_id = 1;

관련된 글 (mysql > lecture-mysql)