SQL 쿼리를 가장 효율적으로 실행하기 위해 DB가 내부적으로 실행 계획을 최적화하는 컴포넌트
옵티마이저의 실행 계획을 비교하는 기준
| 항목 | 설명 |
|---|---|
| I/O 비용 | 디스크에서 데이터를 읽어오는 비용 (랜덤/순차 접근 등) |
| CPU 비용 | 정렬, 필터링, 조인 연산 등에 소요되는 계산량 |
| 메모리 사용량 | 정렬, 해시 조인 등에 필요한 메모리 공간 |
| 네트워크 비용 (분산 DB) | 노드 간 데이터 전송량 |
옵티마이저가 실행 계획을 세울 때 참고하는 테이블/인덱스/컬럼에 대한 메타 정보
| 항목 | 설명 |
|---|---|
| 테이블의 총 행 수 | COUNT(*) 수준의 추정값 |
| 인덱스의 유니크도(Cardinality) | 특정 컬럼값이 얼마나 다양하게 분포돼 있는지 |
| NULL 여부 | 특정 컬럼에 NULL이 얼마나 있는지 |
| 값의 범위 | MIN, MAX 값 등 |
| 데이터 분포 히스토그램 (MySQL 8+) | 특정 컬럼값이 어떻게 분포돼 있는지 상세히 기록 |
옵티마이저가 접근 경로를 결정하는 요소
| 항목 | 설명 |
|---|---|
| 인덱스 존재 여부 | 해당 컬럼에 인덱스가 존재하는가? |
| 인덱스 유니크도 | 유니크 인덱스일수록 효율 ↑ |
| 인덱스 범위 조건 | =, BETWEEN, LIKE 'abc%' 는 인덱스 활용 가능 |
| 다중 컬럼 인덱스 | 복합 인덱스의 선두 컬럼을 조건으로 쓸 수 있는가? |
| 인덱스 커버 여부 | SELECT 대상 컬럼이 모두 인덱스에 포함돼 있으면 → 커버링 인덱스 |
EXPLAIN SELECT * FROM Employees WHERE Department = 'Sales';
EXPLAIN 뒤에 오는 쿼리의EXPLAIN ANALYZE는 MySQL 8.0+에서 예상 비용과 실제 실행 정보까지 보여줌SELECT /*+ INDEX(Employees dept_idx) */ *
FROM Employees
WHERE Department = 'Sales';
/*+ HINT */ 형태로 옵티마이저의 동작을 유도INDEX(table index_name) 특정 인덱스 사용하도록 유도NO_INDEX_MERGE() 인덱스 병합 사용 안 함STRAIGHT_JOIN JOIN 순서 강제 지정ANALYZE TABLE Employees;
ANALYZE TABLE 로 테이블에 대해 실행 계획용 통계를 다시 계산대량 데이터 INSERT/DELETE 이후
인덱스 추가 후에도 성능 변화 없음
쿼리 성능이 갑자기 나빠짐
동일 쿼리인데 실행 시간에 편차가 큼
➡️ 트래픽 적은 시간대에 실행!
SELECT *
FROM A
STRAIGHT_JOIN B ON A.id = B.a_id;
SELECT /*+JOIN_FIXED_ORDER */ *
FROM A
JOIN B ON A.id = B.a_id
JOIN C ON C.id = A.a_id;
STRAIGHT_JOIN/*+ JOIN_FIXED_ORDER */