Skip to content
난이도 문제은행 6

쿼리 힌트 ​

쿼리 힌트(query hint)는 옵티마이저(optimizer)가 내린 결정을 사람이 덮어쓰는 지시문이다. 원인을 진단해 확신이 있을 때만 쓴다. 근거 없이 막 넣은 힌트는 데이터가 바뀌는 날 더 나쁜 계획을 강제하는 독이 된다.

인덱스 힌트 (구문형) ​

인덱스 힌트(index hint)는 테이블 이름 뒤에 붙여, 그 테이블에서 어떤 인덱스를 쓸지 옵티마이저에게 알려 주는 구문이다.

sql
SELECT * FROM t USE INDEX (ix_a) WHERE ...;    -- 이 인덱스 후보로
SELECT * FROM t FORCE INDEX (ix_a) WHERE ...;  -- 테이블 스캔을 아주 비싸게 보고 이 인덱스 쪽으로 강제
SELECT * FROM t IGNORE INDEX (ix_a) WHERE ...; -- 이 인덱스 제외
  • USE: 이 인덱스를 후보로 올리라는 제안이다. 옵티마이저는 그래도 풀 테이블 스캔(full table scan)을 고를 수 있다.
  • FORCE: USE와 같되, 테이블 스캔을 아주 비싸다고 가정하게 만든다. 그래서 지정한 인덱스를 쓸 수 있으면 테이블 스캔이 더 유리해 보여도 인덱스를 쓴다. 지정한 인덱스를 쓸 방법이 아예 없는 조건일 때만 테이블 스캔으로 간다.
  • IGNORE: 특정 인덱스를 후보에서 뺀다. 옵티마이저가 엉뚱한 인덱스를 잘못 고를 때 쓴다.

옵티마이저 힌트 (주석형, 8.0) ​

sql
SELECT /*+ NO_INDEX(t ix_a) */ * FROM t ...;      -- 인덱스 제외
SELECT /*+ JOIN_ORDER(s, b) */ ...;                -- 조인 순서 고정
SELECT /*+ NO_BNL(t) */ ...;                       -- t의 해시 조인 비활성화(8.4)
SELECT /*+ SET_VAR(sort_buffer_size=4M) */ ...;    -- 이 문장에만 변수 적용

옵티마이저 힌트(optimizer hint)는 /*+ ... */ 주석 안에 쓰는 지시다. 조인 순서·조인 방식·서브쿼리(subquery) 처리 전략까지 세밀하게 건드릴 수 있다. NO_BNL은 옛 조인 방식인 BNL(Block Nested Loop, 블록 중첩 루프)에서 이름을 땄지만, 8.4에서는 해시 조인(hash join)을 끄는 힌트다. SET_VAR는 시스템 변수(system variable) 값을 쿼리 한 문장에만 바꿔 적용한다. 서버나 세션 설정을 건드리지 않으므로 실험·우회에 안전하다.

쓸 때 원칙 ​

  1. EXPLAIN으로 원인을 먼저 찾는다. EXPLAIN은 옵티마이저가 고른 실행 계획(execution plan)을 보여 주는 명령이다. 통계가 낡은 게 원인이면 통계를 다시 모으는 ANALYZE TABLE이 답이지 힌트가 아니다.
  2. 범위를 좁게 잡는다. 특정 인덱스를 강제하는 FORCE보다 문제 인덱스만 빼는 IGNORE가, 전역 설정 변경보다 한 문장에만 적용하는 SET_VAR가 낫다.
  3. 이유를 주석으로 남긴다. 왜 넣었는지 적혀 있지 않은 힌트는 다음 사람이 지워도 되는지 판단할 수 없어 못 지운다.
  4. 정기적으로 재검토한다. 데이터 분포가 바뀌면 예전에 맞던 힌트가 오히려 나쁜 계획을 강제하는 독이 된다.

면접 포인트 ​

  • "USE와 FORCE의 차이?" → 제안 vs 테이블 스캔을 사실상 배제하는 강제(인덱스를 쓸 수 없으면 그래도 스캔).
  • "힌트를 언제 써요?" → 진단 후 확신 있을 때. 통계·인덱스가 먼저.
  • "SET_VAR가 뭐예요?" → 쿼리 한정 변수 적용. 우회 실험용.

관련 문서