본문 바로가기
CS/SQL

Index Merge

by HWK 2026. 1. 6.

1️⃣ Index Merge란?

Index Merge는 MySQL/MariaDB 옵티마이저가:

“단일 인덱스로는 최적화가 안 되니까
여러 인덱스를 각각 타서
결과를 합쳐보자”

라고 판단했을 때 선택하는 실행 계획이다.

EXPLAIN에 보통 이렇게 나타난다.

type: index_merge
Extra: Using intersect(...)

2️⃣ 옵티마이저가 Index Merge를 선택하는 상황

테이블에 이런 인덱스가 있을 때

-- 인덱스
IDX_A (COMPCD)
IDX_B (YEARXX)

-- 쿼리
WHERE COMPCD = ?
  AND YEARXX = ?
  • 단일 복합 인덱스 ❌
  • 두 개의 단일 인덱스만 존재

👉 옵티마이저는:

  • COMPCD 인덱스 스캔
  • YEARXX 인덱스 스캔
  • 두 결과의 교집합 계산

3️⃣ 왜 느린가? (핵심 이유 5가지)

① “두 번 이상 읽는다”

  • 인덱스 A 전체 스캔
  • 인덱스 B 전체 스캔

👉 단일 복합 인덱스는 한 번만 탐색


② 임시 집합 계산 비용

  • PK 목록을 메모리에 저장
  • 교집합/합집합 계산

👉 이 자체가 CPU + 메모리 비용


③ LIMIT / early stop 불가

Index Merge는:

  • 조건 만족 row를 전부 수집
  • 그 후에야 LIMIT 적용

👉 페이징 / EXISTS / 커서와 궁합 최악


④ Covering Index 효과 없음

  • 결국 PK 목록만 얻음
  • 테이블 접근은 그대로 발생

👉 랜덤 I/O 줄어들지 않음


⑤ 데이터 분포 변화에 취약

  • 인덱스 통계 조금만 틀려도
  • 옵티마이저 판단 급변

👉 실행 계획 안정성 ❌


4️⃣ Index Merge vs 복합 인덱스 (비교)

항목Index Merge복합 인덱스
인덱스 탐색 여러 번 한 번
정렬 지원
LIMIT 최적화
Covering Index
실행 계획 안정성 낮음 높음

5️⃣ Index Merge가 특히 치명적인 패턴

❌ AND 조건 + 페이징

❌ EXISTS / 서브쿼리


6️⃣ “Index Merge가 나쁘기만 한가?” (예외)

⚠️ 아주 제한적인 경우

  • 단일 인덱스만 있고
  • 결과 집합이 매우 작고
  • 임시 최적화가 필요할 때

👉 하지만 이마저도:

  • UNION ALL
  • 쿼리 분리

가 더 나은 경우가 많다.


7️⃣ 실무에서의 원칙 (중요)

Index Merge가 보이면
“인덱스가 부족하다는 신호”로 받아들여야 한다.

 


8️⃣ 결론

Index Merge는
“여러 인덱스를 쓰는 최적화”가 아니라
“복합 인덱스가 없어서 어쩔 수 없이 쓰는 차선책”이다.


9️⃣ 질문

만약 적절한 인덱스가 있는데, Index Merge가 시행되는 경우도 있을까?

👉 “알맞은 인덱스가 있음에도 Index Merge가 실행되는 경우는 실제로 존재한다.”
다만 그건 **인덱스 설계 문제가 아니라 ‘옵티마이저 판단 문제’**인 경우가 대부분

 

1. 가장 흔한 경우: 옵티마이저가 “더 싸다고 착각”할 때

-- 복합 인덱스 (정답)
IDX_C (COMPCD, PLCBUS, YEARXX)

-- 단일 인덱스들
IDX_A (COMPCD)
IDX_B (PLCBUS)

-- 쿼리
WHERE COMPCD = ?
  AND PLCBUS = ?

👉 사람 입장:
“복합 인덱스 IDX_C 타는 게 당연하지 않나?”

👉 옵티마이저 입장(통계가 틀리면):

  • COMPCD = ? → 결과 많음
  • PLCBUS = ? → 결과 많음
  • “둘을 merge 하는 게 싸겠는데?”

➡️ Index Merge 선택

 

2. OR / IN 조건이 섞인 경우

WHERE COMPCD = ?
  AND (PLCBUS = ? OR YEARXX = ?)
  • 복합 인덱스가 있어도
  • OR 조건 때문에 인덱스 경로가 갈라짐

👉 옵티마이저는:

  • PLCBUS 인덱스 탐색
  • YEARXX 인덱스 탐색
  • 결과 merge

➡️ Index Merge 강제 발생

📌 이건 인덱스 문제가 아니라 쿼리 구조 문제

 


🔟 Index Merge를 없애기

1. OR → UNION ALL (가장 강력)

OR가 느려지는 핵심 이유

OR는 조건이 “갈라지기” 때문에:

  • 한 인덱스로 한 번에 못 푸는 경우가 많고
  • 옵티마이저가
    • Index Merge(여러 인덱스 결과 합치기)
    • filesort/temp
      쪽으로 빠지기 쉽습니다.

UNION ALL이 빨라지는 핵심 이유

UNION ALL은 아예 쿼리를 둘로 나눠서:

  • 각각이 자기 조건에 맞는 최적 인덱스를 타고
  • 결과를 그냥 이어 붙입니다(ALL이니까 중복제거 없음)

즉,

OR: “DB가 알아서 합쳐줘(근데 합치는 비용이 큼)”
UNION ALL: “내가 분기해줄게, 너는 각자 인덱스만 잘 타”

이 차이입니다.

 

2. IN(대량) → JOIN(임시 집합)

IN 값이 수십~수백 이상 커지면, 내부적으로 비용이 커지고 계획이 흔들립니다.

리팩토링

  • 값 목록을 임시 테이블/파생 테이블로 만들고 JOIN
  • (애플리케이션에서 리스트를 테이블로 넣을 수 있으면 더 좋음)

✅ 옵티마이저가 더 안정적으로 플랜을 고름
✅ 실행 계획이 덜 흔들림

 

3. 함수/가공된 조건 제거 (SARGable 만들기)

인덱스가 있어도 아래와 같은 조건이면 의미 없음

WHERE DATE(REGDT) = '2026-01-06'

아래같이 작성해야 함

WHERE REGDT >= '2026-01-06 00:00:00'
  AND REGDT <  '2026-01-07 00:00:00'

✅ 인덱스 레인지 스캔 가능
✅ filesort/merge 위험 감소

 

4. 정렬/페이징을 먼저 줄이기 (2단계 조회)

Index Merge가 뜨는 큰 이유 중 하나가
“조건 + 정렬 + LIMIT”을 한 번에 못 풀기 때문입니다.

추천 구조

  1. 커버링 인덱스로 PK 30개만 추림
  2. PK로 본문 30개만 가져오기
-- 1단계 (커버링)
SELECT DOCSEQ
FROM HERGMT
WHERE COMPCD = ?
  AND PLCBUS = ?
ORDER BY DOCSEQ DESC
LIMIT 30;

-- 2단계
SELECT *
FROM HERGMT
WHERE DOCSEQ IN ( ...30개... );

✅ 정렬 부담 급감
✅ 인덱스 선택이 안정적

'CS > SQL' 카테고리의 다른 글

Covering Index (커버링 인덱스)  (0) 2026.01.06
인덱스 설계 사고방식 2  (0) 2026.01.06
인덱스 설계 사고방식 1  (0) 2026.01.06
재귀 쿼리(Recursive Query)  (0) 2025.10.01
SQL 동적 쿼리(Dynamic SQL)  (0) 2025.10.01