인덱스(Index)

mskimdev·2026년 4월 17일

MySQL

목록 보기
11/20
post-thumbnail

인덱스 (INDEX)

행이 수십만 건 쌓인 테이블에서 특정 이메일을 조회한다고 생각해보자. DB는 기본적으로 첫 번째 행부터 마지막 행까지 전부 훑어본다. 데이터가 많을수록 느려지는 건 당연하다. 인덱스(Index)는 이 문제를 해결하기 위해 존재한다.


인덱스란

책 뒤에 붙어 있는 색인을 떠올리면 쉽다. "트랜잭션"이라는 단어가 몇 페이지에 나오는지 찾으려면, 책을 처음부터 읽는 것보다 색인에서 'ㅌ' 항목을 찾는 게 훨씬 빠르다. 인덱스가 정확히 그 역할이다.

DB는 특정 컬럼에 인덱스를 만들어두면, 검색할 때 전체 행을 훑는 대신 인덱스를 통해 원하는 위치로 바로 이동한다. 이를 풀 테이블 스캔(Full Table Scan) 대신 인덱스 스캔(Index Scan)이라고 한다.

단, 인덱스는 공짜가 아니다. 조회 속도는 빨라지지만, 인덱스 자체를 별도로 저장하는 공간이 필요하고, INSERT/UPDATE/DELETE 시에는 인덱스도 함께 갱신해야 해서 쓰기 성능이 떨어진다. 조회가 많고 쓰기가 적은 컬럼에 거는 게 기본 원칙이다.


인덱스의 종류

PRIMARY KEY 인덱스

PRIMARY KEY를 지정하면 MySQL이 자동으로 인덱스를 만든다. 이를 클러스터형 인덱스(Clustered Index)라고 하는데, 실제 데이터가 PRIMARY KEY 순서로 물리적으로 정렬되어 저장된다. 테이블에 하나만 존재한다.

별도로 생성할 필요 없이, PRIMARY KEY 선언만으로 자동 생성된다.

CREATE TABLE members (
    member_id   INT         PRIMARY KEY AUTO_INCREMENT, -- 자동으로 인덱스 생성
    member_name VARCHAR(50) NOT NULL
);

UNIQUE 인덱스

UNIQUE 제약 조건을 걸면 자동으로 고유 인덱스가 생성된다. PRIMARY KEY 인덱스와 구조는 같지만, NULL을 허용하고 여러 개를 만들 수 있다는 점이 다르다.

CREATE TABLE members (
    member_id   INT          PRIMARY KEY AUTO_INCREMENT,
    email       VARCHAR(100) NOT NULL UNIQUE -- 자동으로 UNIQUE 인덱스 생성
);

일반 인덱스

중복을 허용하는 일반적인 인덱스다. 자주 조회하는 컬럼, WHERE 절에 자주 등장하는 컬럼에 직접 생성한다.

-- 테이블 생성 시 함께 만들기
CREATE TABLE members (
    member_id   INT         PRIMARY KEY AUTO_INCREMENT,
    member_name VARCHAR(50) NOT NULL,
    INDEX idx_name (member_name)
);

-- 이미 있는 테이블에 추가하기
CREATE INDEX idx_name ON members (member_name);

member_name으로 검색하는 쿼리가 많다면 이렇게 인덱스를 걸어두면 된다.

-- 이 쿼리가 빨라진다
SELECT * FROM members WHERE member_name = '김민수';

복합 인덱스

두 개 이상의 컬럼을 묶어서 만드는 인덱스다. 여러 조건을 동시에 쓰는 쿼리에 효과적이다.

CREATE INDEX idx_name_email ON members (member_name, email);

복합 인덱스는 순서가 중요하다. (member_name, email)로 만든 인덱스는 member_name 단독 검색이나 member_name + email 검색에는 사용되지만, email 단독 검색에는 사용되지 않는다. 인덱스의 앞 컬럼부터 순서대로 사용되는 구조이기 때문이다.


인덱스 확인과 삭제

현재 테이블에 어떤 인덱스가 걸려 있는지 확인하려면 SHOW INDEX를 쓴다.

SHOW INDEX FROM members;

인덱스를 삭제할 때는 DROP INDEX를 사용한다.

DROP INDEX idx_name ON members;

EXPLAIN으로 인덱스 사용 여부 확인

쿼리 앞에 EXPLAIN을 붙이면 MySQL이 실제로 인덱스를 사용하고 있는지 확인할 수 있다.

EXPLAIN SELECT * FROM members WHERE member_name = '김민수';

결과의 type 컬럼이 ALL이면 풀 테이블 스캔, refrange 등이면 인덱스를 사용하고 있다는 뜻이다. rows 컬럼은 DB가 몇 개의 행을 읽을 것으로 예상하는지 보여주는데, 인덱스가 없을 때와 있을 때의 차이를 직접 비교해볼 수 있다.


언제 인덱스를 걸어야 하는가

인덱스를 무조건 많이 걸면 좋을 것 같지만, 실제로는 판단이 필요하다.

인덱스가 유용한 경우:

  • WHERE 절에 자주 등장하는 컬럼
  • JOIN에서 연결 조건으로 쓰이는 컬럼 (FOREIGN KEY 컬럼)
  • ORDER BY, GROUP BY에 자주 쓰이는 컬럼
  • 데이터가 많고 조회 빈도가 높은 테이블

인덱스를 피해야 하는 경우:

  • 행이 몇 백 건밖에 없는 작은 테이블 (풀 스캔이 오히려 빠름)
  • INSERT/UPDATE/DELETE가 매우 빈번한 컬럼
  • TRUE/FALSE, 성별처럼 값의 종류가 거의 없는 컬럼 (중복이 많으면 인덱스 효과가 없음)

인덱스는 조회 성능을 높이는 대신 쓰기 비용과 저장 공간을 내는 구조다. 필요한 곳에 적절하게 거는 것이 핵심이다.

profile
<- 개발 공부하는 나

0개의 댓글