IT/SQL·DB

인덱스를 걸었는데도 느린 이유 7가지 | 실무에서 자주 틀리는 포인트

PARK_90 2026. 3. 25. 22:03
300x250

SQL 성능 문제를 처음 잡을 때 가장 많이 듣는 말이 있습니다.

"인덱스 걸었는데 왜 아직도 느리지?"

실제로 실무에서는 인덱스를 추가했는데도 쿼리가 기대만큼 빨라지지 않는 경우가 정말 많습니다. 심지어 어떤 경우에는 인덱스를 걸고도 풀스캔(Full Scan) 이 나거나, 오히려 성능이 더 나빠지기도 합니다.

즉, 중요한 건 단순히 인덱스를 걸었느냐가 아니라, DB가 그 인덱스를 제대로 탈 수 있는 조건인지 입니다.

먼저 핵심부터
인덱스가 있다고 무조건 빠른 것이 아닙니다.
조건절 작성 방식, 함수 사용, 복합 인덱스 순서, 조회 건수, 정렬/그룹핑, 통계 정보 상태에 따라 인덱스를 못 타거나 안 타는 경우가 많습니다.
그래서 실무에서는 인덱스 존재 여부보다 실행계획과 조건절 구조를 같이 봐야 합니다.

이번 글에서는 인덱스를 걸었는데도 느린 대표적인 이유 7가지를 실무 관점에서 쉽게 정리해보겠습니다.

왜 이런 일이 생길까?
DB는 단순히 인덱스가 있다고 항상 그 길을 선택하지 않습니다.

데이터베이스 옵티마이저는 여러 실행 방법 중에서 비용(cost) 이 더 낮다고 판단하는 방식을 선택합니다.

즉,

  • 인덱스가 있어도
  • 인덱스를 타는 비용이 더 크다고 판단되면
  • DB는 그냥 테이블 전체를 읽는 방식을 선택할 수 있습니다.

그래서 성능 문제를 볼 때는 항상 아래 순서로 생각하는 게 좋습니다.

  1. 인덱스가 있는가?
  2. 인덱스를 탈 수 있는 조건인가?
  3. 탈 수 있어도 실제로 타는가?
  4. 실행계획상 왜 그 경로를 골랐는가?
실무 포인트
인덱스를 만들었다인덱스를 활용한다는 전혀 다른 이야기입니다.
1) WHERE 절에서 컬럼을 가공했다
인덱스 컬럼에 함수가 걸리면 인덱스를 제대로 못 타는 경우가 많습니다.

가장 흔한 실수 중 하나입니다.

예를 들어 created_at 컬럼에 인덱스가 있어도 아래처럼 쓰면 문제가 됩니다.

SELECT *
FROM orders
WHERE DATE(created_at) = '2026-03-25';

이 경우 DB는 인덱스 컬럼 원본값이 아니라 함수가 적용된 결과를 비교해야 해서, 인덱스를 비효율적으로 사용하거나 아예 못 탈 수 있습니다.

더 좋은 방식은 이렇게 범위 조건으로 바꾸는 것입니다.

SELECT *
FROM orders
WHERE created_at >= '2026-03-25 00:00:00'
  AND created_at < '2026-03-26 00:00:00';
핵심 요약
인덱스 컬럼을 함수로 감싸면 인덱스 효율이 크게 떨어질 수 있습니다.
2) 복합 인덱스 순서를 잘못 이해했다
복합 인덱스는 아무 컬럼부터 자유롭게 타는 구조가 아닙니다.

예를 들어 (status, created_at) 순서로 복합 인덱스가 있다고 해보겠습니다.

그런데 쿼리가 이렇게 생기면:

SELECT *
FROM orders
WHERE created_at >= '2026-03-01';

앞쪽 컬럼인 status 조건이 없기 때문에 인덱스를 기대만큼 못 쓸 수 있습니다.

복합 인덱스는 보통 왼쪽부터(left-most) 활용된다고 이해하면 쉽습니다.

즉,

  • (status, created_at) 인덱스면
  • status 조건이 중요하고
  • 그 다음에 created_at이 따라오는 구조입니다.
실무 포인트
복합 인덱스는 컬럼이 포함돼 있느냐보다 순서가 맞느냐가 더 중요할 때가 많습니다.
3) 조회 대상이 너무 많다
결과 건수가 많으면 인덱스를 타는 게 오히려 손해일 수 있습니다.

인덱스는 보통 일부만 빨리 찾을 때 강합니다.

그런데 조건을 걸어도 전체 데이터의 30%, 50%, 80%를 읽어야 한다면 어떨까요? 이 경우 DB는 인덱스를 타고 다시 테이블을 많이 읽는 것보다, 그냥 한 번에 전체를 읽는 게 더 싸다고 판단할 수 있습니다.

예를 들어 gender, is_deleted, status 같은 선택도가 낮은 컬럼은 인덱스를 걸어도 기대보다 효과가 약할 수 있습니다.

기억할 점
많이 읽는 쿼리는 인덱스보다 풀스캔이 더 효율적일 수 있습니다.
4) LIKE 검색을 잘못 썼다
앞부분 와일드카드는 B-Tree 인덱스에 치명적입니다.

예를 들어 아래 쿼리는 많이 느려집니다.

SELECT *
FROM member
WHERE name LIKE '%park';

앞에 %가 붙으면 문자열 시작점을 알 수 없기 때문에 일반적인 B-Tree 인덱스를 제대로 활용하기 어렵습니다.

반면 아래처럼 접두사 검색은 인덱스를 탈 가능성이 큽니다.

WHERE name LIKE 'park%'

즉,

  • park% → 인덱스 활용 가능성 높음
  • %park → 인덱스 활용 어려움
5) ORDER BY / GROUP BY 때문에 추가 비용이 생긴다
조회 조건만 인덱스로 맞춰도, 정렬/집계 단계에서 다시 느려질 수 있습니다.

예를 들어 WHERE 절은 인덱스를 탔는데, 정렬 기준이 인덱스 순서와 맞지 않으면 DB는 별도로 정렬(Sort) 작업을 해야 할 수 있습니다.

즉,

  • WHERE는 빠른데
  • ORDER BY에서 느리고
  • 실행계획상 filesort / sort cost가 커지는 상황이 생길 수 있습니다.

복합 인덱스를 설계할 때는 조회 조건 + 정렬 조건을 같이 봐야 하는 이유가 여기에 있습니다.

6) 통계 정보가 오래됐거나 실행계획이 잘못 잡혔다
옵티마이저 판단의 출발점은 통계 정보입니다.

옵티마이저는 데이터 분포를 추정해서 실행계획을 정합니다. 그런데 통계 정보가 오래됐거나 데이터 편차가 크면, 실제보다 잘못 추정해서 비효율적인 실행계획을 고를 수 있습니다.

즉,

  • 인덱스는 맞게 있는데
  • 옵티마이저가 잘못 판단해서
  • 엉뚱한 인덱스를 타거나 풀스캔을 선택할 수 있습니다.

실무에서는 이런 경우

  • 통계 갱신
  • 실행계획 비교
  • 힌트 사용 여부 검토 를 같이 보게 됩니다.
7) SELECT * 때문에 랜덤 I/O가 커진다
인덱스를 타더라도 결국 본문 데이터를 많이 다시 읽으면 느릴 수 있습니다.

인덱스는 조건을 찾는 데는 유리하지만, 실제 조회 컬럼이 너무 많으면 결국 테이블 본문(데이터 페이지)까지 많이 접근해야 합니다.

특히 SELECT *

  • 필요 없는 컬럼까지 다 가져오고
  • 인덱스만으로 해결되지 않아서
  • 추가 I/O 비용을 키우는 경우가 많습니다.

그래서 실무에서는 가능하면

  • 필요한 컬럼만 조회하고
  • 경우에 따라 커버링 인덱스(covering index) 를 고려합니다.
실무 팁
성능 문제를 볼 때는 인덱스를 탔는지만 보지 말고, 그 뒤에 얼마나 많은 데이터를 다시 읽는지까지 봐야 합니다.
한눈에 정리하면
인덱스를 걸었는데도 느린 이유는 대부분 아래 패턴으로 모입니다.
번호 느린 이유 핵심 포인트
1 컬럼 가공 함수 사용으로 인덱스 효율 저하
2 복합 인덱스 순서 왼쪽 컬럼부터 맞아야 함
3 조회 건수 과다 풀스캔이 더 유리할 수 있음
4 LIKE 사용 방식 앞부분 % 는 인덱스 활용 어려움
5 정렬/집계 비용 ORDER BY, GROUP BY 에서 느려질 수 있음
6 통계 정보 문제 옵티마이저가 잘못 판단할 수 있음
7 SELECT * 추가 I/O 비용 증가
실무에서는 어떻게 보면 좋을까?
인덱스 문제는 결국 실행계획과 데이터 분포를 같이 보는 습관이 중요합니다.

실무에서는 보통 이렇게 확인합니다.

  • 실행계획에서 실제 어떤 경로를 탔는지 확인
  • 조건절에 함수/형변환이 있는지 확인
  • 복합 인덱스 순서가 맞는지 확인
  • 정렬/집계 단계에서 추가 비용이 큰지 확인
  • 조회 건수가 너무 많은지 확인
  • 통계 정보가 오래되지 않았는지 점검

즉, 인덱스 성능은 인덱스 생성 자체보다 쿼리 구조와 실행계획 해석 능력이 더 중요합니다.

실전 한 줄
인덱스를 걸었는데도 느리다는 말은 대개 인덱스를 못 타는 구조거나, 타도 이득이 없는 구조라는 뜻입니다.
마무리 정리
인덱스는 성능 개선의 핵심 도구지만, 만능 해결책은 아닙니다. 컬럼 가공, 복합 인덱스 순서, 선택도, LIKE 사용 방식, 정렬 비용, 통계 정보, SELECT * 같은 요소가 겹치면 인덱스를 걸고도 충분히 느릴 수 있습니다.
핵심 질문 = 인덱스가 있는가?
다음 질문 = 인덱스를 탈 수 있는가?
진짜 질문 = 실행계획상 왜 이 경로를 선택했는가?
인덱스 튜닝은 인덱스를 더 만드는 작업이 아니라, DB가 인덱스를 잘 쓸 수 있는 구조로 쿼리를 바꾸는 작업에 가깝습니다.
728x90