개발블로그

[인덱스] MySQL 인덱스란? 개념, 장단점과 기본 설계 기준 본문

STUDY/SQL

[인덱스] MySQL 인덱스란? 개념, 장단점과 기본 설계 기준

devmel 2026. 7. 24. 23:29
Contents 접기

데이터가 적은 테이블은 모든 행을 확인하더라도 조회 시간이 오래 걸리지 않는다.

 

하지만 데이터가 수십만 건, 수백만 건으로 증가하면 조건에 맞는 데이터를 찾기 위해 테이블 전체를 읽는 작업이 큰 비용으로 이어질 수 있다. 

 

 이때 원하는 데이터를 더 빠르게 찾기 위해 사용하는 것이 인덱스(Index)


인덱스 개념

특정 데이터를 빠르게 찾을 수 있도록 별도로 만들어 놓은 정렬된 검색용 자료구조

 

인덱스에서 검색값 확인

→  검색값에 해당하는 데이터 위치 확인

→ 필요한 행 조회

 

책의 색인을 생각하면 이해하기 쉬운데, 

책에서 특정 단어가 나오는 페이지를 찾기 위해 처음부터 모든 페이지를 읽을 필요는 없음.
책 뒤에 있는 색인에서 단어를 찾고, 표시된 페이지로 이동하면 된다.

 

/**
다음과 같은 usrs 테이블이 있다고 가정

id	name	email
1	철수	chulsoo@example.com
2	영희	younghee@example.com
3	민수	minsoo@example.com
*/

-- 이메일로 사용자를 검색하는 쿼리
SELECT *
FROM users
WHERE email = 'user@example.com';

/**
email 인덱스가 없는 경우)

1번 행의 email 확인
→ 2번 행의 email 확인
→ 3번 행의 email 확인
→ ...
→ 조건에 맞는 행 발견

테이블의 데이터를 순서대로 읽으면서 조건을 검사 (= 전체 테이블 스캔)   


email 인덱스가 있는 경우)

email 인덱스에서 user@example.com 검색
→ 해당 사용자의 위치 확인
→ 필요한 행 조회
*/

 


인덱스가 있다고 항상 사용되는 것은 아님

MySQL의 옵티마이저는 쿼리를 실행할 수 있는 여러 방법의 비용을 비교하고,

더 효율적이라고 판단한 실행 방법을 선택함.

 

EX] 

전체 사용자 중 90%가 활성 사용자라고 할 때

SELECT *
FROM users
WHERE status = 'active';

 

status 컬럼에 인덱스가 있더라도 조건에 해당하는 사용자가 테이블의 90%라면 결국 대부분의 행을 조회해야 함.

 

이 경우에는 다음 두 방법의 비용이 비교됨 

1. 방법1 : status 인덱스에서 active 항목 탐색 → 각 인덱스 항목에 저장된 기본 키 확인 → 기본키를 이용해 실제 행을 반복 조회 

2. 방법2 : users 테이블 전체를 순서대로 읽기 → status가 active인 행 반환

 

Q) 그래도 전체 데이터가 아니라 90%만 가져오는 인덱스가 더 효율적이지 않을까?

더보기
더보기

A) 반드시 그렇지는 않다. 

인덱스로 조회하는 90%는 실제 행 데이터를 바로 가져오는 것이 아니라, 

먼저 인덱스에서 조건에 맞는 항목을 찾은 뒤 해당 항목이 가리키는 실제 행을 다시 조회해야 한다.

 

전체 테이블 스캔

페이지 1 → 페이지 2 → 페이지 3 → 페이지 4
보조 인덱스를 통한 실제 행 조회

인덱스에서 id 95 확인  → 데이터 페이지 8
인덱스에서 id 210 확인 → 데이터 페이지 17
인덱스에서 id 31 확인  → 데이터 페이지 3
인덱스에서 id 502 확인 → 데이터 페이지 42

 

 


데이터는 페이지 단위로 읽는다

데이터베이스는 일반적으로 디스크에서 한 행을 하나씩 개별적으로 읽지 않는다.

여러 행이나 인덱스 항목을 묶은 페이지(Page) 단위로 데이터를 읽고 관리함.

 

따라서 조회 성능을 판단할 때는 결과로 반환되는 행의 개수뿐 아니라, 

해당 행을 찾기 위해 얼마나 많은 인덱스 페이지와 데이터 페이지를 읽어야 하는지도 중요함.

 

적절한 인덱스는 읽어야 하는 페이지의 범위를 줄여 디스크 I/O를 감소시킬 수 있음

 


인덱스의 장단점

장점

조회 범위 감소

테이블 전체가 아니라 조건에 맞는 데이터 범위만 탐색할 수 있음

 

ex)

SELECT *
FROM users
WHERE email = 'user@example.com';

전체 사용자 1,000,000명 → 조건에 맞는 사용자 1명

 

정렬 비용 감소

쿼리의 정렬 순서가 인덱스 순서와 맞으면 별도의 정렬 작업을 줄일 수 있다.

 

JOIN 탐색 비용 감소

연결 컬럼의 인덱스를 이용해 다른 테이블의 관련 행을 빠르게 찾을 수 있다.

 

ex)

SELECT users.name, orders.amount
FROM users
JOIN orders
  ON users.id = orders.user_id;

orders.user_id에 인덱스가 있다면 특정 사용자의 주문이 있는 위치를 빠르게 탐색할 수 있다.

 

단점

저장 공간 증가

인덱스는 테이블과 별도의 자료구조로 저장됨

그러므로, 인덱스를 추가 할 수록 → 디스크에 저장해야 하는 데이터도 증가.

※ 특히, 긴 문자열 컬럼이나 여러 컬럼을 포함하는 복합 인덱스는 인덱스 크기가 커질 수 있음.

 

데이터 변경 비용 증가

데이터 추가 ·수정·삭제 시 관련 인덱스도 함께 변경해야 한다.

INSERT 테이블에 새로운 행 추가 → 관련 인덱스에도 새로운 항목 추가
UPDATE 테이블의 컬럼값 변경 → 해당 컬럼과 관련된 인덱스 항목 수정
DELETE 테이블에서 행 삭제 → 관련 인덱스 항목도 삭제

 


인덱스 설계

기본 판단 기준 

판단 기준 인덱스를 고려하기 좋은 경우 신중하게 검토할 경우
읽는 데이터 범위를 줄일 수 있는가? - WHERE, JOIN에 사용되며 조건 결과가 적은 경우
- 특정 값을 검색했을 때 중복이 적은 경우
- 조건 결과가 테이블의 대부분인 경우
- 넓은 범위를 조회하는 경우
- 값의 종류가 적고 각 값의 비율이 비슷한 경우
인덱스의 정렬 구조를 활용할 수 있는가? WHERE, ORDER BY, GROUP BY, JOIN의 컬럼과 순서가 인덱스 구조에 맞는 경우 - 인덱스 컬럼에 함수, 연산을 적용하는 경우
- 복합 인덱스의 앞쪽 컬럼을 건너뛰는 경우
- 앞에 %가 붙는 LIKE 
유지 비용보다 조회 이점이 큰가? - 크기가 큰 테이블에서 자주 실행되는 핵심 조회
- 읽기가 데이터 변경보다 많은 경우
- 거의 실행되지 않는 쿼리
- 작은 테이블
- INSERT, UPDATE, DELETE 가 많은 경우
- 기존 인덱스와 역할이 중복되는 경우 

 

읽는 데이터 범위를 줄일 수 있는가?

 

이메일 처럼 중복이 적은 컬럼은 특정 값을 검색했을 때 결과가 적을 가능성이 높음. 

SELECT *
FROM users
WHERE email = 'user@example.com';

 

반면 조건에 맞는 행이 전체의 대부분이라면 인덱스가 있어도 읽는 범위를 충분히 줄이지 못한다. 

-- 전체 사용자의 90%가 active라면 인덱스보다 전체 테이블 스캔이 더 효율적일 수 있다.
SELECT *
FROM users
WHERE status = 'active';

 

다만, 값의 종류가 적다고 항상 인덱스가 불필요한 것은 아님

SELECT *
FROM jobs
WHERE status = 'failed';

=> status의 값이 몇 종류뿐이여도 failed가 전체의 0.1%라면 검색 범윌르 크게 줄일 수 있음.

 

 

인덱스의 정렬 구조를 활용할 수 있는가?

인덱스는 특정 컬럼과 순서에 따라 정렬되어 있음.

따라서 쿼리의 조건이나 정렬 방식이 인덱스 구조와 맞아야 효과적으로 활용할 수 있음.

EX]

CREATE INDEX idx_articles_status_created
ON articles (status, created_at);

-- 다음 쿼리는 인덱스의 컬럼 순서와 잘 맞는다.
SELECT *
FROM articles
WHERE status = 'published'
  AND created_at >= '2026-07-01';
  
  -- 반면 다음 조건은 인덱스의 정렬 구조를 충분히 활용하기 어려울 수 있다.
  WHERE DATE(created_at) = '2026-07-24'; -- 인덱스 컬럼에 함수 적용
  WHERE created_at >= '2026-07-01' -- 복합 인덱스의 앞쪽 컬럼을 사용하지 않음
  WHERE name LIKE '%kim%' -- 문자열의 시작 부분을 알 수 없음

 

 

유지 비용보다 조회 이점이 큰가?

인덱스는 조회 속도를 높일 수 있지만, 별도의 저장 공간을 사용하고 데이터 변경 시 함께 갱신됨.