DB 성능 튜닝을 이야기할 때 절대 빠질 수 없는 존재가 있다.
바로 옵티마이저(Query Optimizer) 이다.
많은 개발자들이 "인덱스가 있는데 왜 느리지?" 라고 생각하지만,
실제 실행 계획을 결정하는 것은 옵티마이저다.
옵티마이저는 우리가 작성한 SQL을 분석해서
가장 비용이 낮다고 판단되는 실행 계획(Execution Plan) 을 선택하는 엔진이다.
핵심 역할:
즉,
SQL을 실제로 어떻게 실행할지 정하는 두뇌
MSSQL은 Cost-Based Optimizer (비용 기반 옵티마이저) 를 사용한다.
옵티마이저는 실제 데이터를 읽지 않고
통계 정보를 기반으로 "예상 비용"을 계산한다.
👉 그래서 통계가 틀리면 실행 계획도 틀릴 수 있다.
Ctrl + L
Ctrl + M
또는
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
많이 겪는 사례다.
예:
SELECT *
FROM Orders
WHERE OrderDate >= '2024-01-01'
인덱스가 있어도 Scan이 나올 수 있는 이유:
옵티마이저는
"인덱스를 타는 것보다 그냥 전체 스캔이 더 싸다"고 판단할 수 있다.
옵티마이저의 핵심은 예상 Row 수 계산이다.
예상 Row 수가 틀리면:
예:
SELECT *
FROM Orders o
JOIN OrderDetails d ON o.Id = d.OrderId
예상 Row 수가 적다고 판단하면 Nested Loop 선택
많다고 판단하면 Hash Join 선택
👉 실제 Row 수와 예상 Row 수 차이가 크면 성능 급락
옵티마이저는 통계를 기반으로 판단한다.
통계 확인:
DBCC SHOW_STATISTICS ('Orders', 'IX_Orders_OrderDate');
통계 업데이트:
UPDATE STATISTICS Orders;
또는
EXEC sp_updatestats;
통계가 오래되면
옵티마이저는 잘못된 실행 계획을 선택할 수 있다.
MSSQL 옵티마이저의 대표적인 이슈.
프로시저 첫 실행 시 전달된 파라미터 값을 기준으로
실행 계획을 캐시한다.
예:
CREATE PROC GetOrders
@CustomerId INT
AS
BEGIN
SELECT *
FROM Orders
WHERE CustomerId = @CustomerId
END
처음 실행 값에 따라 실행 계획이 고정된다.
해결 방법:
옵티마이저는 상황에 따라 다음 중 하나를 선택한다.
👉 개발자가 직접 선택하는 것이 아니라
옵티마이저가 비용 계산 후 결정
예:
SELECT *
FROM Orders WITH (INDEX(IX_Orders_OrderDate))
힌트는 옵티마이저 판단을 강제로 바꾼다.
하지만:
👉 힌트는 최후의 수단
옵티마이저는
SQL Server에서 가장 중요한 성능 결정 요소다.
우리가 SQL을 작성하지만
실제 실행 전략은 옵티마이저가 정한다.
튜닝의 시작은
실행 계획을 읽는 것이다.