카테고리 없음

MySQL 인덱스(Index)란? 데이터베이스 검색 속도를 높이는 원리와 활용법

14722 2026. 8. 24. 02:00

MySQL 인덱스(Index)란? 데이터베이스 검색 속도를 높이는 원리와 활용법

MySQL 데이터베이스에 저장된 데이터가 많아질수록 검색 속도가 느려질 수 있습니다.

처음에는 수백 개의 데이터만 있어서 빠르게 실행되던 SQL이 데이터가 수십만 건, 수백만 건으로 증가하면서 갑자기 느려지는 경우가 있습니다.

이때 데이터베이스 성능을 개선하기 위해 가장 먼저 확인하는 것 중 하나가 바로 MySQL 인덱스(Index)입니다.

인덱스는 책의 맨 뒤에 있는 찾아보기와 비슷한 역할을 합니다. 원하는 데이터를 찾을 때 테이블의 모든 데이터를 처음부터 끝까지 확인하지 않고, 인덱스를 이용해 필요한 위치를 빠르게 찾을 수 있도록 도와줍니다.

이번 글에서는 MySQL 인덱스의 원리, 인덱스가 필요한 이유, CREATE INDEX 사용법, EXPLAIN으로 인덱스 확인하는 방법, 복합 인덱스와 주의사항까지 초보자도 이해하기 쉽게 정리해보겠습니다.


1. MySQL 인덱스란 무엇인가?

MySQL 인덱스는 데이터베이스에서 원하는 데이터를 더 빠르게 검색할 수 있도록 만들어 놓은 별도의 자료 구조입니다.

예를 들어 회원 데이터가 100만 건 있다고 생각해보겠습니다.

다음 SQL을 실행합니다.

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

인덱스가 없다면 데이터베이스가 많은 행을 확인해야 할 수 있습니다.

반면 email 컬럼에 적절한 인덱스가 있다면 MySQL이 인덱스를 이용해 필요한 데이터를 더 효율적으로 찾을 수 있습니다.

쉽게 표현하면 다음과 같습니다.

인덱스 없음

100만 개 데이터
↓
처음부터 하나씩 확인
↓
원하는 데이터 찾기

인덱스가 있다면:

인덱스
↓
검색 조건에 맞는 위치 탐색
↓
필요한 데이터 접근

따라서 대량의 데이터를 검색하는 서비스에서는 인덱스 설계가 매우 중요합니다.


2. 인덱스는 책의 찾아보기와 비슷하다

인덱스를 이해하기 가장 쉬운 방법은 책을 생각하는 것입니다.

500페이지짜리 책에서 특정 단어를 찾는다고 가정해보겠습니다.

인덱스가 없다면 책의 1페이지부터 마지막 페이지까지 직접 찾아야 합니다.

하지만 책 뒤에 색인이 있다면:

"데이터베이스"
→ 125페이지
→ 278페이지
→ 410페이지

처럼 원하는 위치를 빠르게 찾을 수 있습니다.

MySQL 인덱스도 비슷한 역할을 합니다.

테이블 데이터
     ↑
     │
인덱스
     │
검색 조건

다만 데이터베이스의 인덱스는 단순한 목록이 아니라 검색에 최적화된 자료 구조를 사용합니다.


3. MySQL 인덱스의 대표적인 구조

MySQL의 InnoDB에서는 일반적인 인덱스가 주로 B-Tree 계열의 구조를 기반으로 동작합니다.

쉽게 이해하면 데이터가 무작위로 나열되어 있는 것이 아니라 검색하기 좋은 구조로 정리되어 있다고 생각하면 됩니다.

예를 들어 숫자 데이터가:

10
20
30
40
50
60
70
80
90

처럼 정리되어 있다면 특정 값을 찾을 때 모든 값을 하나씩 확인하는 것보다 효율적으로 탐색할 수 있습니다.

실제 MySQL 내부 동작은 훨씬 복잡하지만 초보 단계에서는 "검색에 적합하도록 데이터를 정리한 별도의 구조"라고 이해하면 충분합니다.


4. 인덱스가 없으면 어떤 문제가 생길까?

다음과 같은 users 테이블이 있다고 가정하겠습니다.

CREATE TABLE users (
    id INT,
    name VARCHAR(100),
    email VARCHAR(255)
);

데이터가 100만 건 있다고 가정합니다.

다음 SQL을 실행합니다.

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

email에 적절한 인덱스가 없다면 MySQL은 많은 행을 확인해야 할 수 있습니다.

이러한 방식으로 전체 테이블을 확인하는 것을 일반적으로 Full Table Scan이라고 합니다.

데이터가 적을 때는 문제가 크지 않을 수 있습니다.

하지만 데이터가 수백만 건으로 증가하면 검색 시간이 길어질 가능성이 커집니다.


5. 인덱스 생성하는 기본 방법

특정 컬럼에 인덱스를 만들려면 CREATE INDEX를 사용할 수 있습니다.

예를 들어 email 컬럼에 인덱스를 추가합니다.

CREATE INDEX idx_users_email
ON users(email);

이제 email을 이용한 검색에서 해당 인덱스를 활용할 가능성이 생깁니다.

인덱스 이름은 일반적으로 다음처럼 작성하면 관리하기 편합니다.

idx_테이블명_컬럼명

예:

idx_users_email
idx_orders_customer_id
idx_products_category_id

6. PRIMARY KEY도 인덱스다

MySQL에서 PRIMARY KEY를 지정하면 해당 키에 대한 인덱스가 만들어집니다.

예:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(255)
);

여기서:

id INT PRIMARY KEY

의 id는 기본키이면서 인덱스와 관련된 핵심적인 역할을 합니다.

따라서 별도로:

CREATE INDEX idx_users_id
ON users(id);

와 같이 동일한 인덱스를 추가하는 것은 일반적으로 필요하지 않습니다.


7. UNIQUE 인덱스란?

중복되지 않는 값을 관리해야 하는 컬럼에는 UNIQUE를 사용할 수 있습니다.

예를 들어 이메일 주소가 중복되면 안 되는 회원 시스템이라면:

CREATE UNIQUE INDEX idx_users_email
ON users(email);

과 같은 형태를 사용할 수 있습니다.

이렇게 하면 검색 성능뿐만 아니라 중복 데이터 방지라는 목적도 함께 수행할 수 있습니다.

다만 실제 테이블 설계에서는 UNIQUE 제약조건과 인덱스의 관계를 고려해 적절한 방식으로 설계해야 합니다.


8. 인덱스가 실제로 사용되는지 확인하는 방법

인덱스를 만들었다고 해서 모든 SQL이 자동으로 빨라지는 것은 아닙니다.

MySQL이 실제로 인덱스를 사용하는지 확인해야 합니다.

이때 사용하는 명령어가 바로:

EXPLAIN

입니다.

예:

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

실행하면 SQL의 실행 계획을 확인할 수 있습니다.


9. EXPLAIN에서 중요한 항목

처음 EXPLAIN을 접한다면 모든 항목을 외울 필요는 없습니다.

다음 항목부터 확인하면 좋습니다.

항목의미

type 데이터 접근 방식
possible_keys 사용할 가능성이 있는 인덱스
key 실제 선택된 인덱스
rows 예상해서 검사할 행 수
Extra 추가 실행 정보

특히 다음 항목이 중요합니다.

possible_keys
key
rows

예를 들어:

possible_keys: idx_users_email
key: idx_users_email

처럼 표시된다면 해당 인덱스가 실행 계획에서 선택됐다는 의미입니다.


10. key가 NULL이라고 무조건 문제가 있는 것은 아니다

EXPLAIN 결과에서:

key: NULL

이라고 나오면 인덱스를 사용하지 않았다는 의미입니다.

하지만 이것이 무조건 잘못된 것은 아닙니다.

데이터가 아주 적거나 전체 데이터를 읽는 것이 오히려 더 효율적인 상황에서는 MySQL이 인덱스를 사용하지 않을 수도 있습니다.

따라서:

key = NULL이면 무조건 인덱스를 추가해야 한다.

라고 생각하면 안 됩니다.

실행 계획과 실제 데이터 양, 쿼리 조건을 함께 확인해야 합니다.


11. WHERE 조건에 사용하는 컬럼과 인덱스

인덱스를 가장 많이 사용하는 곳 중 하나가 WHERE 조건입니다.

예:

SELECT *
FROM orders
WHERE customer_id = 100;

customer_id를 기준으로 검색하는 요청이 매우 많고 데이터가 많다면 다음과 같은 인덱스를 검토할 수 있습니다.

CREATE INDEX idx_orders_customer_id
ON orders(customer_id);

다시 실행 계획을 확인합니다.

EXPLAIN
SELECT *
FROM orders
WHERE customer_id = 100;

인덱스 추가 전후의 EXPLAIN 결과를 비교하는 것이 중요합니다.


12. ORDER BY에도 인덱스를 활용할 수 있다

인덱스는 검색뿐만 아니라 정렬과 관련된 작업에서도 도움이 될 수 있습니다.

예를 들어:

SELECT *
FROM orders
ORDER BY order_date DESC;

처럼 날짜순으로 정렬하는 요청이 매우 많다면 order_date에 대한 인덱스를 검토할 수 있습니다.

CREATE INDEX idx_orders_order_date
ON orders(order_date);

하지만 모든 ORDER BY가 자동으로 인덱스를 사용하는 것은 아닙니다.

WHERE 조건, 정렬 조건, 데이터 분포 등에 따라 실행 계획이 달라질 수 있기 때문에 EXPLAIN으로 확인해야 합니다.


13. 복합 인덱스란?

두 개 이상의 컬럼을 묶어서 하나의 인덱스로 만드는 것을 복합 인덱스(Composite Index)라고 합니다.

예를 들어:

CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);

이렇게 만들 수 있습니다.

이 인덱스는 다음과 같은 조건에서 활용될 가능성이 있습니다.

SELECT *
FROM orders
WHERE customer_id = 100
ORDER BY order_date DESC;

복합 인덱스는 컬럼의 순서가 매우 중요합니다.


14. 복합 인덱스의 컬럼 순서가 중요한 이유

다음과 같은 인덱스가 있다고 가정합니다.

CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);

인덱스 순서는:

customer_id
      ↓
order_date

입니다.

따라서 단순히 컬럼 두 개를 묶었다고 생각하면 안 됩니다.

어떤 컬럼을 먼저 배치할 것인지가 실제 쿼리 패턴과 관련이 있습니다.

예를 들어 서비스에서:

WHERE customer_id = ?
AND order_date >= ?

형태의 검색이 매우 많다면 이런 조건을 고려하여 복합 인덱스를 설계할 수 있습니다.


15. 가장 중요한 왼쪽부터 사용 원칙

복합 인덱스를 이해할 때 흔히 설명하는 개념이 왼쪽부터 사용되는 특성(Leftmost Prefix)입니다.

다음 인덱스가 있다고 가정합니다.

CREATE INDEX idx_user_name_age
ON users(name, age);

인덱스 순서는:

name → age

입니다.

따라서 name을 조건으로 사용하는 쿼리에서는 활용 가능성이 높습니다.

WHERE name = '홍길동'

또는:

WHERE name = '홍길동'
AND age = 30

등입니다.

반면 age만 조건으로 사용하는 쿼리는 이 복합 인덱스를 충분히 활용하지 못할 수 있습니다.

따라서 복합 인덱스를 만들 때는 실제 서비스에서 어떤 조건으로 검색하는지를 먼저 분석해야 합니다.


16. LIKE와 인덱스

문자열 검색에서도 인덱스 사용 여부를 주의해야 합니다.

다음과 같은 쿼리를 생각해보겠습니다.

SELECT *
FROM users
WHERE name LIKE '김%';

이처럼 앞부분이 고정된 검색은 상황에 따라 인덱스를 활용할 수 있습니다.

하지만:

SELECT *
FROM users
WHERE name LIKE '%김%';

처럼 앞에 %가 붙으면 일반적인 B-Tree 인덱스를 효율적으로 사용하기 어려울 수 있습니다.

검색 기능이 많은 웹사이트라면 이런 부분을 함께 확인해야 합니다.


17. 인덱스가 많으면 무조건 좋을까?

그렇지 않습니다.

인덱스를 많이 만든다고 해서 데이터베이스가 무조건 빨라지는 것은 아닙니다.

인덱스는 별도의 저장 공간을 사용합니다.

또한 데이터를 추가하거나 수정하거나 삭제할 때 관련 인덱스도 함께 관리해야 합니다.

즉:

인덱스 증가
↓
검색에는 도움이 될 수 있음
↓
저장 공간 증가
↓
INSERT / UPDATE / DELETE 비용 증가 가능

따라서 필요한 인덱스만 만들어야 합니다.


18. 사용하지 않는 인덱스도 점검해야 한다

운영 기간이 길어지면 처음에는 필요했던 인덱스가 더 이상 사용되지 않을 수도 있습니다.

테이블에 인덱스가 어떤 것들이 있는지 확인하려면:

SHOW INDEX FROM users;

를 사용할 수 있습니다.

인덱스가 지나치게 많이 만들어져 있다면 실제 쿼리 패턴과 실행 계획을 확인한 후 정리 여부를 검토할 수 있습니다.


19. 인덱스 삭제 방법

불필요한 인덱스를 삭제할 때는 다음과 같은 형태를 사용할 수 있습니다.

DROP INDEX idx_users_email
ON users;

다만 운영 서버에서 인덱스를 바로 삭제하는 것은 주의해야 합니다.

특히 다른 SQL에서 해당 인덱스를 사용하고 있을 가능성이 있기 때문입니다.

삭제 전에는 반드시:

SHOW INDEX FROM users;

와 실제 쿼리의 EXPLAIN 결과 등을 확인하는 것이 좋습니다.


20. 인덱스와 데이터 양의 관계

인덱스의 효과는 데이터 양에 따라 달라질 수 있습니다.

데이터가 100건인 테이블이라면 인덱스가 없어도 매우 빠르게 조회될 수 있습니다.

하지만 데이터가:

1,000건
10만 건
100만 건
1,000만 건

으로 증가하면 검색 방식의 차이가 커질 수 있습니다.

따라서 서비스를 처음 만들 때부터 앞으로 데이터가 얼마나 증가할지를 고려하여 데이터베이스 구조를 설계하는 것이 좋습니다.


21. 인덱스를 무조건 추가하기 전에 확인할 것

느린 SQL을 발견했다면 다음 순서로 점검해보세요.

① 실제로 느린 SQL인지 확인

Slow Query Log나 애플리케이션의 성능 데이터를 확인합니다.

② EXPLAIN 실행

EXPLAIN SELECT ...;

③ 검사하는 행의 수 확인

rows

값이 매우 큰지 확인합니다.

④ 기존 인덱스 확인

SHOW INDEX FROM 테이블명;

⑤ 적절한 인덱스 검토

WHERE, JOIN, ORDER BY 등의 조건을 분석합니다.

⑥ 추가 후 다시 EXPLAIN

인덱스 생성 전후의 실행 계획을 비교합니다.


22. JOIN에서도 인덱스가 중요하다

실제 서비스에서는 하나의 테이블만 조회하는 것보다 여러 테이블을 JOIN하는 경우가 많습니다.

예:

SELECT u.name, o.order_date
FROM users u
JOIN orders o
ON u.id = o.customer_id;

이런 경우 JOIN에 사용되는 컬럼의 인덱스도 성능에 영향을 줄 수 있습니다.

예를 들어:

CREATE INDEX idx_orders_customer_id
ON orders(customer_id);

와 같은 인덱스를 검토할 수 있습니다.

단, 실제 효과는 데이터 구조와 실행 계획에 따라 달라집니다.


23. 인덱스 성능 개선 실전 예제

다음과 같은 orders 테이블이 있다고 가정해보겠습니다.

orders
--------------------------------
id
customer_id
order_date
product_id
amount

그리고 다음 SQL이 자주 실행됩니다.

SELECT id, order_date, amount
FROM orders
WHERE customer_id = 100
ORDER BY order_date DESC;

먼저 실행 계획을 확인합니다.

EXPLAIN
SELECT id, order_date, amount
FROM orders
WHERE customer_id = 100
ORDER BY order_date DESC;

그 후 실제 데이터와 쿼리 패턴을 분석하여 다음과 같은 복합 인덱스를 검토할 수 있습니다.

CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);

다시:

EXPLAIN
SELECT id, order_date, amount
FROM orders
WHERE customer_id = 100
ORDER BY order_date DESC;

를 실행하여 변화가 있는지 확인합니다.

중요한 것은 인덱스를 만드는 것 자체가 아니라 실제 쿼리 성능이 개선됐는지 확인하는 것입니다.


24. MySQL 인덱스 확인 명령어 모음

테이블의 인덱스 확인

SHOW INDEX FROM users;

실행 계획 확인

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

인덱스 생성

CREATE INDEX idx_users_email
ON users(email);

복합 인덱스 생성

CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);

인덱스 삭제

DROP INDEX idx_users_email
ON users;

25. MySQL 인덱스 관리 시 주의사항

인덱스를 설계할 때는 다음 사항을 기억해두면 좋습니다.

첫째, 인덱스를 무조건 많이 만들지 않습니다.

필요한 검색 패턴을 기준으로 설계해야 합니다.

둘째, 실제 SQL을 기준으로 판단합니다.

테이블에 데이터가 많다는 이유만으로 인덱스를 추가하지 말고 EXPLAIN으로 확인합니다.

셋째, 복합 인덱스의 컬럼 순서를 신중하게 결정합니다.

쿼리에서 어떤 조건을 자주 사용하는지 분석해야 합니다.

넷째, 인덱스가 쓰기 성능에도 영향을 줄 수 있다는 점을 기억합니다.

INSERT, UPDATE, DELETE가 많은 테이블에서는 인덱스 수를 더욱 신중하게 관리해야 합니다.

다섯째, 운영 서버에서는 변경 전후를 비교합니다.

인덱스 생성 전후의 실행 계획과 실제 응답 시간을 확인하는 것이 좋습니다.


26. MySQL 인덱스 핵심 정리

MySQL 인덱스는 데이터베이스에서 원하는 데이터를 빠르게 찾을 수 있도록 도와주는 중요한 기능입니다.

가장 기본적인 인덱스 생성 방법은 다음과 같습니다.

CREATE INDEX idx_users_email
ON users(email);

현재 인덱스를 확인하려면:

SHOW INDEX FROM users;

SQL 실행 계획은:

EXPLAIN SELECT ...;

복합 인덱스는:

CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);

사용하지 않는 인덱스를 삭제할 때는:

DROP INDEX idx_users_email
ON users;

를 사용할 수 있습니다.


마무리

MySQL 인덱스는 데이터가 많아질수록 더욱 중요해지는 데이터베이스 성능 관리 기능입니다.

하지만 인덱스가 많다고 데이터베이스가 무조건 빨라지는 것은 아닙니다.

잘못 설계된 인덱스는 저장 공간을 증가시키고 데이터 변경 작업에도 추가적인 비용을 발생시킬 수 있습니다.

따라서 가장 좋은 방법은 다음 순서로 접근하는 것입니다.

느린 SQL 발견
      ↓
EXPLAIN 실행
      ↓
실행 계획 분석
      ↓
WHERE / JOIN / ORDER BY 확인
      ↓
기존 인덱스 확인
      ↓
필요한 인덱스 설계
      ↓
인덱스 생성
      ↓
성능 변화 재측정

특히 앞서 살펴본 MySQL Slow Query Log와 EXPLAIN을 함께 활용하면 어떤 SQL이 느린지 찾고 그 원인을 분석하는 데 큰 도움이 됩니다.

결국 MySQL 성능 최적화의 핵심은 무조건 인덱스를 추가하는 것이 아니라 실제 사용되는 SQL과 데이터 구조를 분석해 필요한 곳에 적절한 인덱스를 설계하는 것입니다.