fos-blog/study
01 / 홈02 / 카테고리03 / 시리즈
01 / 홈02 / 카테고리03 / 시리즈

카테고리

  • AI 페이지로 이동
    • RAG 페이지로 이동
    • langgraph 페이지로 이동
    • agents.md
    • BMAD Method — AI 에이전트로 애자일 개발하는 방법론
    • Claude Code 메모리: CLAUDE.md와 .claude/rules를 규칙으로 쓰는 법
    • Claude Code의 Skill 시스템 - 개발자를 위한 AI 자동화의 새로운 차원
    • Claude Code를 5주 더 쓴 결과 — 스킬·CLAUDE.md를 키워가는 방식
    • Claude Code를 11일 동안 쓴 결과 — 데이터로 본 나의 사용 패턴
    • Claude Code 멀티 에이전트 — Teams
    • AI 에이전트와 디자인의 새 컨벤션 — DESIGN.md, Google Stitch, Claude Design
    • Docling — IBM Research 의 문서 파싱 toolkit 상세 정리
    • 하네스 엔지니어링 실전 — 4인 에이전트 팀으로 코딩 파이프라인 구축하기
    • 하네스 엔지니어링 — 오래 실행되는 AI 에이전트를 위한 설계
    • 멀티모달 LLM (Multimodal Large Language Model)
    • AI 에이전트와 함께 MVP 만들기 — dooray-cli 사례
    • 스킬 문서를 신경망처럼 학습시킨다 — Microsoft SkillOpt 분석
  • ai 페이지로 이동
    • agent 페이지로 이동
    • [초안] AI 제품 백엔드 안정성 — 지연·비용·권한·관측·도구 실패·폴백/재시도/사람 에스컬레이션
    • [초안] LLM 평가 프레임워크: 골든셋, 회귀 테스트, LLM-as-a-judge, 사람 피드백 루프
  • algorithm 페이지로 이동
    • live-coding 페이지로 이동
    • 분산 계산을 위한 알고리즘
  • apartment 페이지로 이동
    • 구리 럭키아파트 24평 인테리어 레퍼런스 모음
  • architecture 페이지로 이동
    • [초안] 시니어 백엔드를 위한 API 설계 실전 스터디 팩 — REST · 멱등성 · 페이지네이션 · 버전 전략
    • [초안] API Versioning과 Backward Compatibility: 시니어 백엔드 관점 정리
    • 캐시 설계 전략 총정리
    • [초안] CJ푸드빌 디지털 채널 면접: 슬롯 도메인 경험을 커머스 도메인 설계 능력으로 번역하기
    • [초안] 커머스 Spring 서비스에 Clean/Hexagonal Architecture를 실용적으로 적용하기
    • [초안] 커머스 도메인 모델링: 주문·재고·노출의 세 축을 분리해서 설계하기
    • 커머스 주문 상태와 데이터 정합성 기본기 — CJ푸드빌 면접 대비
    • [초안] 쿠폰/프로모션 동시성과 정합성 기본기 — 선착순·중복 사용 방지·발급/사용/복구
    • [초안] DDD와 도메인 모델링: 시니어 백엔드 관점의 전술/전략 패턴 실전 가이드
    • [초안] Decorator & Chain of Responsibility — 행동을 체인으로 조립하는 두 가지 방식
    • 디자인 패턴
    • [초안] 분산 아키텍처 완전 정복: Java 백엔드 시니어 인터뷰 대비 실전 가이드
    • [초안] 분산 트랜잭션과 Outbox 패턴 — 왜 2PC를 피하고 어떻게 대신할 것인가
    • 분산 트랜잭션
    • [초안] e-Commerce 주문·결제 도메인 모델링: 상태머신, 멱등성, Outbox/Saga 실전 정리
    • [초안] F&B 쿠폰·프로모션·멤버십·포인트 설계
    • [초안] F&B · e-Commerce 디지털 채널 도메인 한 장 정리 — CJ푸드빌 디지털 채널 백엔드 면접 대비
    • [초안] F&B 주문/매장/픽업 상태머신 설계 — CJ푸드빌 디지털 채널 백엔드 관점
    • [초안] F&B 이커머스 결제·환불·정산 운영 가이드
    • [초안] Hexagonal / Clean Architecture를 Spring 백엔드에 적용하기
    • [초안] 대규모 커머스 트래픽 처리 패턴 — 1,600만 고객과 올영세일을 버티는 설계
    • [초안] 레거시 JSP/jQuery 화면과 신규 API가 공존하는 백엔드 운영 전략
    • [초안] MSA 서비스 간 통신: Redis [Cache-Aside](../database/redis/cache-aside.md) × Kafka 이벤트 하이브리드 설계
    • [초안] Observability 입문: 시니어 백엔드가 장애를 탐지하고 대응하는 방식
    • [초안] Outbox / Inbox Pattern 심화 — 분산 메시징의 정합성 문제를 DB 트랜잭션으로 풀어내기
    • [초안] 결제 도메인 멱등성과 트랜잭션 재시도 기본기
    • [초안] 시니어 백엔드를 위한 Resilience 패턴 실전 가이드 — Timeout, Retry, Circuit Breaker, Bulkhead, Backpressure
    • [초안] REST API 버저닝과 모바일 앱 하위 호환성 — CJ푸드빌 디지털 채널 백엔드 관점
    • [초안] Spring Batch vs Event-Driven — 같은 비동기처럼 보이지만 전혀 다른 두 패러다임
    • [초안] Strategy Pattern — 분기문을 없애는 설계, 시니어 백엔드 인터뷰 핵심 패턴
    • [초안] 시니어 백엔드를 위한 시스템 설계 입문 스터디 팩
    • [초안] 템플릿 메서드 패턴 - 백엔드 처리 골격을 강제하는 가장 오래되고 가장 위험한 패턴
    • [초안] 대규모 트래픽 중 무중단 마이그레이션 — Feature Flag + Shadow Mode 실전
  • database 페이지로 이동
    • mysql 페이지로 이동
    • opensearch 페이지로 이동
    • redis 페이지로 이동
    • 김영한의-실전-데이터베이스-설계 페이지로 이동
    • [초안] DB Connection Pool Saturation과 Thread Pool 격리
    • 커넥션 풀 크기는 얼마나 조정해야 할까?
    • 인덱스 - DB 성능 최적화의 핵심
    • [초안] JPA N+1과 커머스 조회 모델: 주문/메뉴/쿠폰 도메인에서 살아남기
    • [초안] MyBatis 기본기 — XML Mapper, resultMap, 동적 SQL, 운영 패턴 정리
    • [초안] MyBatis와 JPA/Hibernate 트레이드오프 — 레거시 백엔드를 다루는 시니어 관점
    • 역정규화 (Denormalization)
    • 데이터 베이스 정규화
  • devops 페이지로 이동
    • docker 페이지로 이동
    • k8s 페이지로 이동
    • k8s-in-action 페이지로 이동
    • observability 페이지로 이동
    • [초안] 커머스/F&B 채널 장애 첫 5분과 관측성 기본기
    • Envoy Proxy
    • [초안] F&B / e-Commerce 운영 장애 대응과 모니터링 — 백엔드 관점 정리
    • Graceful Shutdown
    • [초안] 시니어 백엔드를 위한 SLO와 Error Budget 기반 장애 대응
  • finance 페이지로 이동
    • industry-cycle 페이지로 이동
    • investing 페이지로 이동
  • http 페이지로 이동
    • HTTP Connection Pool
    • HTTPS는 어떻게 안전한가 — TLS, 인증서, 그리고 termination
  • interview 페이지로 이동
    • [초안] AI 서비스 팀 경험 기반 시니어 백엔드 면접 질문 뱅크 — Spring Batch RAG / gRPC graceful shutdown / 전략 패턴 / 12일 AI 웹툰 MVP
    • [초안] 커머스/F&B 면접 답변집 — 슬롯 도메인 경험을 주문·결제·쿠폰·매장 설계로 매핑하기
    • [초안] F&B / e-Commerce 운영 모니터링과 장애 대응 인터뷰 정리
    • Observability — 면접 답변 프레임
    • [초안] 시니어 Java 백엔드 면접 마스터 플레이북 — 김병태
    • [초안] NSC 슬롯팀 경험 기반 질문 은행 — 도메인 모델링·동시성·성능·AI 협업
  • java 페이지로 이동
    • concurrency 페이지로 이동
    • jdbc 페이지로 이동
    • opentelemetry 페이지로 이동
    • spring 페이지로 이동
    • spring-batch 페이지로 이동
    • 더_자바_코드를_조작하는_다양한_방법 페이지로 이동
    • [초안] Java 동시성 락 정리 — 커머스 메뉴/프로모션 정책 캐시 갱신 관점
    • [초안] JVM 튜닝 실전: 메모리 구조부터 Virtual Threads, GC 튜닝, 프로파일링까지
    • Java의 로깅 환경
    • MDC (Mapped Diagnostic Context)
    • Java StampedLock — 읽기 폭주에도 쓰기가 밀리지 않는 락
    • Virtual Thread와 Project Loom
  • javascript 페이지로 이동
    • typescript 페이지로 이동
    • AbortController
    • Async Iterator와 제너레이터
    • CommonJS와 ECMAScript Modules
    • 제너레이터(Generator)
    • Http Client
    • Node 백엔드 운영 패턴 — Streams 백프레셔, pipe/pipeline, 멱등성 vs 분산 락
    • Node.js
    • npm vs pnpm — 어떤 기준으로 선택했나
    • `setImmediate()`
  • kafka 페이지로 이동
    • [초안] Kafka 기본 개념 — 토픽, 파티션, 오프셋, 복제
    • Kafka를 사용하여 **데이터 정합성**은 어떻게 유지해야 할까?
    • [초안] Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프
    • 메시지 전송 신뢰성
  • linux 페이지로 이동
    • fsync — 리눅스 파일 동기화 시스템 콜
    • tmux — Terminal Multiplexer
  • network 페이지로 이동
    • L2(스위치)와 L3(라우터)의 역할 차이
    • L4와 VIP(Virtual IP Address)
    • IP Subnet
  • python 페이지로 이동
    • Python async/await — CompletableFuture·Reactor 와 다른 점, 그리고 blocking I/O 함정
    • Python 의존성 관리 — Java Maven/Gradle 사용자가 만나는 첫 충격
    • FastAPI 기초 — Spring Boot 사용자가 빠르게 익히는 법
    • GPU·CUDA·MPS 기초 — 자바 백엔드 개발자가 처음 만나는 그림
    • Multi-process GPU 워크로드 — 자바 ThreadPool 사용자가 만나는 모델 차이
    • Java 개발자를 위한 Python 심화 — OOP·데코레이터·컨텍스트 매니저
    • PyTorch 기초 — 텐서, 디바이스, 그리고 모델 로딩이 무거운 이유
    • Java 개발자를 위한 Python 문법 핵심
    • ML 서비스 성능 분석 워크플로 — 자바 백엔드 트러블슈팅과 다른 점
    • OCR 동작 원리 — Layout · Text · Post-process 3단계
    • Python 서버의 RSS 가 안 줄어드는 이유 — gc.collect 의 한계와 malloc_trim
  • rabbitmq 페이지로 이동
    • [초안] RabbitMQ Basics — 실전 백엔드 관점에서 정리하는 메시지 브로커 기본기
    • [초안] RabbitMQ vs Kafka — 백엔드 메시징 선택 기준과 실전 운영 관점
  • security 페이지로 이동
    • [초안] 시니어 백엔드를 위한 보안 / 인증 스터디 팩 — Spring Security, JWT, OAuth2, OWASP Top 10
    • [초안] Spring Security 6.x OAuth2 + JWT 상용 인증 설계 — Grant 선택, Resource Server, Refresh Rotation, 로그아웃
  • task 페이지로 이동
    • ai-service-team 페이지로 이동
    • nsc-slot 페이지로 이동
    • sb-dev-team 페이지로 이동
    • the-future-company 페이지로 이동
  • testing 페이지로 이동
    • [초안] 시니어 Java 백엔드를 위한 테스트 전략 완전 정리 — 피라미드부터 TestContainers, 마이크로벤치, Contract까지
  • travel 페이지로 이동
    • 오사카 3박 4일 일정표: 우메다 쇼핑, USJ, 난바·도톤보리, 오사카성
  • web 페이지로 이동
    • [초안] HTTP / Cookie / Session / Token 인증 기본기 — 레거시 JSP와 모바일 API가 공존하는 백엔드 관점
FOS-BLOG · FOOTERall systems normal·v0.1 · 2026.04.27·seoul, kr
Ffos-blog/study

개발 학습 기록을 정리하는 블로그입니다. 공부하면서 기록하고, 기록하면서 다시 배웁니다.

visitors
01site
  • Home↗
  • Posts↗
  • Categories↗
  • About↗
02policy
  • 소개/about
  • 개인정보처리방침/privacy
  • 연락처/contact
03categories
  • AI↗
  • Algorithm↗
  • DB↗
  • DevOps↗
  • Java/Spring↗
  • JS/TS↗
  • React↗
  • Next.js↗
  • System↗
04connect
  • GitHub@jon890↗
  • Source repositoryjon890/fos-study↗
  • RSS feed/rss.xml↗
  • Newsletter매주 1 회 · 한 편의 글→
© 2026 FOS Study. All posts MIT-licensed.
built with·Next.js·Tailwind v4·Geist·Pretendard·oklch
fos-blog/database/[초안] MySQL 옵티마이저 힌트 — 인덱…
db

[초안] MySQL 옵티마이저 힌트 — 인덱스 힌트와 optimizer hint로 실행 계획을 다루는 법

이 문서는 MySQL이 고른 실행 계획이 마음에 들지 않을 때, 무엇을 어떻게 강제할 수 있는지를 정리한 학습 가이드다. 결론부터 말하면 힌트는 두 계열로 나뉜다. - 인덱스 힌트(USE / FORCE / IGNORE INDEX) — 오래된 문법, 인덱스 후보 집합만 손댄다. - 옵티마이저 힌트(/+ ... /) — MySQL 5.7+ 문법, 조인 방식·접...

2026.06.11·7 min read·6 views

이 문서의 목표

이 문서는 MySQL이 고른 실행 계획이 마음에 들지 않을 때, 무엇을 어떻게 강제할 수 있는지를 정리한 학습 가이드다. 결론부터 말하면 힌트는 두 계열로 나뉜다.

  • 인덱스 힌트(USE / FORCE / IGNORE INDEX) — 오래된 문법, 인덱스 후보 집합만 손댄다.
  • 옵티마이저 힌트(/*+ ... */) — MySQL 5.7+ 문법, 조인 방식·접근 전략·실행 제어까지 세밀하게 손댄다.

그리고 가장 중요한 운영 원칙: 힌트는 통계로 풀리지 않는 문제에만 쓰는 최후 수단이다. 힌트를 박는 순간 그 쿼리는 데이터 분포 변화를 따라가지 못하게 굳는다.

옵티마이저가 왜 그런 계획을 골랐는지는 쿼리 옵티마이저와 실행 계획 생성에서, 실행 계획을 읽는 법은 EXPLAIN / EXPLAIN ANALYZE에서 다룬다. 본 문서는 그 위에서 "고른 결과를 사람이 어떻게 덮어쓰는가"에 집중한다.


힌트가 두 종류인 이유

MySQL의 힌트는 역사적으로 두 번 진화했다.

계열도입위치손대는 대상
인덱스 힌트오래전부터테이블 참조 바로 뒤인덱스 후보 집합
옵티마이저 힌트5.7 / 8.0에서 확장SELECT 직후 주석 블록조인·접근·서브쿼리·실행 제어

인덱스 힌트는 "이 테이블에서 이 인덱스만 봐라" 수준의 거친 도구다. 옵티마이저 힌트는 주석처럼 생겼지만 MySQL이 파싱하는 정식 문법이고, 적용 범위(scope)를 글로벌·쿼리블록·테이블·인덱스 단위로 지정할 수 있어 적용 범위가 훨씬 좁고 정밀하다.

새 코드라면 옵티마이저 힌트를 우선 검토한다. 인덱스 힌트는 레거시 쿼리 유지보수나 아주 단순한 인덱스 강제에만 남겨둔다.


인덱스 힌트 — USE / FORCE / IGNORE INDEX

세 가지 동작 차이

sql
-- 후보로 "고려만" 하라고 제안 (옵티마이저가 무시할 수도 있음)
SELECT * FROM orders USE INDEX (idx_user_created) WHERE user_id = 100;
 
-- 풀스캔보다 비싸 보여도 이 인덱스를 "쓰게" 강하게 밀어붙임
SELECT * FROM orders FORCE INDEX (idx_user_created) WHERE user_id = 100;
 
-- 이 인덱스는 후보에서 "제외"
SELECT * FROM orders IGNORE INDEX (idx_status) WHERE user_id = 100;

핵심 오해 하나: USE INDEX는 강제가 아니다. 옵티마이저가 그 인덱스보다 풀스캔이 싸다고 판단하면 여전히 풀스캔을 고를 수 있다. 정말 인덱스를 강제하려면 FORCE INDEX를 쓴다.

용도 한정 — FOR JOIN / ORDER BY / GROUP BY

인덱스 힌트는 어느 처리 단계에 적용할지 좁힐 수 있다.

sql
-- 정렬에만 이 인덱스를 쓰도록 한정
SELECT * FROM orders
FORCE INDEX FOR ORDER BY (idx_created_at)
WHERE status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

FOR JOIN은 행을 찾는 접근(조인·WHERE 매칭), FOR ORDER BY는 정렬, FOR GROUP BY는 그룹핑에만 적용된다. 용도를 명시하지 않으면 모든 단계에 적용된다.


옵티마이저 힌트 — /*+ ... */ 구조

기본 문법과 적용 범위

옵티마이저 힌트는 SELECT(또는 INSERT/UPDATE/DELETE) 키워드 바로 다음에 /*+ ... */ 블록으로 넣는다.

sql
SELECT /*+ MAX_EXECUTION_TIME(1000) BNL(o) */
       o.id, o.amount
FROM orders o
WHERE o.user_id = 100;

적용 범위는 4단계로 나뉜다.

  • 글로벌 — 쿼리 전체 (MAX_EXECUTION_TIME 등)
  • 쿼리 블록 — 특정 SELECT 블록 (SEMIJOIN, SUBQUERY 등)
  • 테이블 — 특정 테이블 (BNL(o), NO_BKA(o) 등)
  • 인덱스 — 특정 인덱스 (INDEX(o idx_user), NO_INDEX_MERGE(o idx_a, idx_b) 등)

서브쿼리를 가리키는 QB_NAME

중첩 쿼리에서 "어느 블록"인지 지정하려면 쿼리 블록에 이름을 붙인다.

sql
SELECT /*+ JOIN_ORDER(o, u) */
       u.name, o.amount
FROM users u
JOIN (
  SELECT /*+ QB_NAME(sub) */ user_id, amount
  FROM orders
  WHERE status = 'PAID'
) o ON o.user_id = u.id;

QB_NAME으로 블록에 라벨을 달면, 바깥에서 @블록이름 형태로 그 블록 안의 테이블을 가리킬 수 있다. 중첩이 깊은 쿼리에서 힌트가 "어디에 걸리는지" 모호할 때 필수다.


자주 쓰는 옵티마이저 힌트 카탈로그

대부분 XXX / NO_XXX 쌍으로 존재한다 — 켜기와 끄기.

분류힌트의미
조인 순서JOIN_ORDER, JOIN_PREFIX, JOIN_SUFFIX조인 순서를 고정·부분 고정
조인 알고리즘BNL / NO_BNL, BKA / NO_BKA, HASH_JOIN / NO_HASH_JOINBlock Nested Loop·Batched Key Access·해시 조인 제어
인덱스 접근INDEX, NO_INDEX, GROUP_INDEX, JOIN_INDEX, ORDER_INDEX인덱스 후보 지정 (8.0.20+ 세분화)
인덱스 전략INDEX_MERGE / NO_INDEX_MERGE, MRR / NO_MRR, NO_ICP인덱스 머지·Multi-Range Read·Index Condition Pushdown 제어
서브쿼리SEMIJOIN / NO_SEMIJOIN, SUBQUERYsemi-join 전략, 머티리얼라이즈 vs exists 변환
실행 제어MAX_EXECUTION_TIME, SET_VAR, RESOURCE_GROUP타임아웃·세션 변수 임시 변경·리소스 그룹
파생 테이블MERGE / NO_MERGE뷰·서브쿼리를 머지할지 머티리얼라이즈할지

자주 실전에서 닿는 두 가지를 짚는다.

sql
-- 이 쿼리만 500ms 넘으면 죽여라 (전역 설정을 건드리지 않고 쿼리 단위 타임아웃)
SELECT /*+ MAX_EXECUTION_TIME(500) */ COUNT(*) FROM big_table WHERE flag = 1;
 
-- 이 쿼리에서만 세션 변수를 잠깐 바꿔라 (SET 후 복구하는 패턴을 한 줄로)
SELECT /*+ SET_VAR(optimizer_switch='index_merge=off') */ *
FROM orders WHERE user_id = 100 OR status = 'PAID';

SET_VAR는 SET 한 줄로 세션 변수를 바꾸고 되돌리는 번거로운 패턴을 쿼리 안에 가둬, 영향 범위를 그 한 문장으로 한정한다.


우선순위와 충돌 규칙

힌트를 섞어 쓰면 어느 쪽이 이기는지 헷갈린다. 규칙은 이렇다.

  • 같은 대상에 인덱스 힌트와 옵티마이저 힌트가 둘 다 걸리면 옵티마이저 힌트가 우선한다.
  • 같은 옵티마이저 힌트가 같은 대상에 중복되면 처음 것만 적용되고 나머지는 무시된다 (경고 발생).
  • 서로 모순되는 힌트는 뒤따라온 힌트가 무시된다.
  • FORCE INDEX로 지정한 인덱스가 쿼리에 실제로 쓸 수 없는 인덱스면, 힌트가 조용히 무시되고 풀스캔으로 떨어질 수 있다.

힌트가 듣지 않을 때 디버깅 순서는 단순하다.

sql
-- 1) 힌트를 넣은 그대로 EXPLAIN
EXPLAIN SELECT /*+ INDEX(o idx_user_created) */ * FROM orders o WHERE o.user_id = 100;
 
-- 2) 무시됐다면 경고를 본다
SHOW WARNINGS;

SHOW WARNINGS는 "힌트 이름이 틀렸다", "그 인덱스는 쓸 수 없다", "중복 힌트라 무시했다" 같은 사유를 직접 알려준다. 힌트가 안 먹으면 추측하지 말고 경고를 먼저 읽는다.


작동 원리 — 힌트는 어느 단계에서 적용되나

힌트는 마법이 아니라 옵티마이저의 특정 단계에 개입하는 입력이다.

쿼리 한 줄이 실행되는 단계는 쿼리 옵티마이저와 실행 계획 생성에서 자세히 다루지만, 힌트 관점에서 핵심만 보면 이렇다.

  • 인덱스 힌트·INDEX 류 힌트 → 옵티마이저가 접근 방법 후보를 만드는 단계에서 후보 집합을 좁히거나 강제한다.
  • JOIN_ORDER 류 → 조인 순서 탐색 단계에서 탐색 공간을 고정한다.
  • BNL / BKA / HASH_JOIN 류 → 조인 알고리즘 선택 단계를 덮어쓴다.
  • SEMIJOIN / SUBQUERY → 서브쿼리 변환 단계의 전략을 지정한다.
  • MAX_EXECUTION_TIME → 옵티마이저가 아니라 실행기(executor) 단계에서 타이머로 작동한다.

그래서 "비용 모델상 불가능한 계획"은 힌트로도 못 만든다. 힌트는 옵티마이저가 고려할 수 있는 후보 안에서 선택을 강제하는 것이지, 없는 실행 경로를 만들어내는 게 아니다. FORCE INDEX가 무시되는 가장 흔한 이유도 이것 — 그 인덱스로는 해당 WHERE 조건을 만족할 수 없어서 애초에 후보가 아니기 때문이다.


흔한 오해

  • 힌트를 박으면 항상 빨라진다 — 아니다. 당장은 빨라져도 데이터 분포가 바뀌면 옛 판단에 굳어 더 느려진다. 힌트는 통계 변화에 약하다.
  • USE INDEX면 그 인덱스를 무조건 쓴다 — 아니다. USE는 제안, FORCE가 강제다.
  • 옵티마이저 힌트는 주석이라 무시돼도 된다 — 문법은 주석 모양이지만 MySQL이 정식 파싱한다. 오타가 나면 SHOW WARNINGS에 경고가 뜨고 그 힌트만 무시된다.
  • 힌트 한 번 넣으면 끝 — 버전 업그레이드 시 옵티마이저가 똑똑해져 힌트가 오히려 방해가 되는 경우가 잦다. 힌트는 업그레이드 회귀 검증 대상이다.
  • FORCE INDEX는 인덱스를 새로 만들어준다 — 아니다. 이미 존재하는 인덱스 중에서 고를 뿐이다. 인덱스 설계 자체는 복합 인덱스 완전 정복을 따른다.

설계·운영 체크포인트

힌트를 쓰기 전에 다음 순서로 자문한다.

  1. 통계가 최신인가 — ANALYZE TABLE로 통계를 갱신하면 힌트 없이 풀리는 경우가 많다. 힌트보다 통계가 먼저다.
  2. 인덱스 설계 문제가 아닌가 — 옵티마이저가 나쁜 인덱스를 고르는 건 종종 더 나은 인덱스가 없다는 신호다.
  3. 쿼리를 다시 쓸 수 있나 — sargable하지 않은 조건, 불필요한 함수 래핑을 고치면 힌트가 필요 없어진다.
  4. 그래도 옵티마이저가 명백히 틀렸나 — optimizer_trace로 잘못된 비용 추정을 증거로 확인한 뒤에만 힌트를 박는다.

힌트를 박기로 했다면 운영 규칙을 함께 남긴다.

  • 힌트를 넣은 쿼리에는 왜 박았는지 주석을 단다 (어떤 통계·trace 근거로, 어떤 버전에서).
  • 힌트가 걸린 쿼리는 MySQL 버전 업그레이드 회귀 목록에 올린다.
  • 가능하면 힌트 대신 인덱스 추가·통계 갱신·쿼리 재작성으로 해결한 뒤 힌트를 제거한다.

점검 질문

스스로 답해보며 이해를 확인한다.

  • 인덱스 힌트와 옵티마이저 힌트의 문법 위치와 적용 범위 차이를 설명할 수 있는가.
  • USE INDEX와 FORCE INDEX의 차이는 무엇이고, 왜 USE가 무시될 수 있는가.
  • 옵티마이저 힌트의 4가지 적용 범위(글로벌·쿼리블록·테이블·인덱스)를 예로 들 수 있는가.
  • 중첩 서브쿼리에서 특정 블록을 가리키려면 무엇을 쓰는가.
  • 인덱스 힌트와 옵티마이저 힌트가 충돌하면 어느 쪽이 이기는가.
  • FORCE INDEX가 조용히 무시되는 대표적 원인은 무엇인가.
  • MAX_EXECUTION_TIME이 옵티마이저가 아니라 실행기 단계에서 작동한다는 게 무슨 뜻인가.
  • 힌트를 박기 전에 먼저 확인해야 할 것 네 가지(통계·인덱스 설계·쿼리 재작성·trace 증거)를 말할 수 있는가.

관련 문서

  • 쿼리 옵티마이저와 실행 계획 생성 — 비용 모델·통계·optimizer_trace
  • EXPLAIN / EXPLAIN ANALYZE 완전 정복 — 실행 계획 출력 읽기
  • 복합 인덱스 완전 정복 — 힌트 이전에 점검할 인덱스 설계
  • B-Tree 인덱스 — 인덱스 구조 기초
on this page
  • 01이 문서의 목표
  • 02힌트가 두 종류인 이유
  • 03인덱스 힌트 — USE / FORCE / IGNORE INDEX
  • 세 가지 동작 차이
  • 용도 한정 — FOR JOIN / ORDER BY / GROUP BY
  • 04옵티마이저 힌트 — /*+ ... */ 구조
  • 기본 문법과 적용 범위
  • 서브쿼리를 가리키는 QB_NAME
  • 05자주 쓰는 옵티마이저 힌트 카탈로그
  • 06우선순위와 충돌 규칙
  • 07작동 원리 — 힌트는 어느 단계에서 적용되나
  • 08흔한 오해
  • 09설계·운영 체크포인트
  • 10점검 질문
  • 11관련 문서

이런 글도

  • [초안] Redis Streams 소비자 그룹 신뢰성 — PEL, 재할당, 멱등성까지
    > 이 문서는 Redis Streams의 소비자 그룹(Consumer Group)이 어떻게 메시지 유실 없이 분산 처리를 보장하는가를 운영·장애 관점에서 정리한다. Stream의 기본 명령어와 Pub/Sub과의 비교는 pub-sub.md에 이미 있으므로, 본 문서는 그 위에서 한 단계 더 들어간다 — 소비자가 죽었을 때 메시지는 어디에 남고, 누가 다시 처...
    🗄️ db
    db
    2026.06.11
  • [초안] Redis Pub/Sub 패턴 심화 — 실전 활용과 메시지 큐와의 경계
    > 이 문서는 Redis Pub/Sub의 동작 원리와 실전 패턴(캐시 무효화, 실시간 이벤트 전파, 세션 클러스터링)을 백엔드 면접 관점에서 정리한다. Pub/Sub과 Stream의 비교는 pub-sub.md에 이미 있으므로 본 문서는 Pub/Sub 단일 채널을 패턴 수준에서 어떻게 쓰는가에 집중하고, Kafka·RabbitMQ와의 선택 기준까지 다룬다....
    🗄️ db
    db
    2026.05.19
  • [초안] MySQL 복제와 페일오버 심화: 운영 관점 deep-dive
    > 이 문서는 MySQL 복제와 샤딩의 후속 deep-dive다. binlog 포맷, GTID 개요, replica lag 원인 같은 기본 개념은 그 hub 문서에서 다루고, 여기서는 장애 시 어떻게 primary가 바뀌고 트래픽이 끊김 없이 이어지는가라는 한 가지 축만 깊게 본다. 읽기 부하 분산은 인덱스 + read replica + @Transacti...
    🗄️ db
    db
    2026.05.16
  • [초안] MySQL 옵티마이저와 실행 계획 생성 — 비용 모델·통계·optimizer_trace 실전 가이드
    대부분의 백엔드 개발자는 EXPLAIN 출력을 읽는 법은 알지만, 그 출력을 만들어내는 옵티마이저가 어떻게 동작하는지는 모른다. 면접에서 "왜 인덱스가 있는데 안 타죠?", "조인 순서는 누가 결정하나요?", "옵티마이저가 잘못된 선택을 할 때 어떻게 강제하나요?" 같은 질문을 받으면 막힌다. 옵티마이저는 SQL 한 문장을 수십\수백 개의 후보 실행 계획으...
    🗄️ db
    db
    2026.05.16

댓글 (0)