dev.syw

느린 쿼리의 원인을 실행 계획에서 찾아 개선하는 방법.

실행 계획과 쿼리 튜닝

같은 결과를 내는 쿼리라도 작성 방식에 따라 속도는 수백 배 차이가 납니다. 실행 계획은 SQL Server가 쿼리를 어떻게 처리하는지 보여주는 설계도입니다. 이걸 읽을 줄 알면 느린 쿼리의 진짜 원인을 짚어낼 수 있습니다. 강좌의 마지막인 이번 레슨에서 앞서 배운 인덱스·조건 작성법을 실전 튜닝으로 종합합니다.

학습 목표

  • 실행 계획을 켜고 읽는 방법을 익힙니다.
  • 인덱스 스캔과 시크의 차이를 이해합니다.
  • SARGable 조건으로 인덱스를 타게 만듭니다.
  • 통계의 역할과 흔한 안티패턴을 파악합니다.

실행 계획 보기

SSMS에서 실제 실행 계획을 켜고(Ctrl+M) 쿼리를 실행하거나, 텍스트로 통계를 확인합니다.

-- 읽은 페이지 수와 시간 측정
SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT * FROM Orders WHERE customer_id = 5;

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

STATISTICS IOlogical reads(읽은 페이지 수)가 핵심 지표입니다. 같은 쿼리에서 이 값이 줄면 튜닝이 잘된 것입니다.

💡 TIP — "예상 실행 계획"은 실행 없이 추정만 보여주고, "실제 실행 계획"은 실행 후 실제 행 수까지 보여줍니다. 예상 행 수와 실제 행 수가 크게 다르면 통계가 낡았거나 조건이 비효율적이라는 강한 신호입니다.

스캔 vs 시크

연산의미보통의 평가
Index Seek인덱스로 필요한 행만 콕 집음좋음
Index Scan인덱스 전체를 훑음상황에 따라
Table/Clustered Scan테이블 전체를 훑음큰 테이블이면 나쁨
Key Lookup비클러스터 인덱스 후 본체 재조회잦으면 병목

소량의 행을 찾는데 큰 테이블에서 Scan 이 보인다면, 적절한 인덱스가 없거나 조건이 인덱스를 타지 못하는 경우입니다. Key Lookup 이 많으면 커버링 인덱스(INCLUDE)로 없앨 수 있습니다.

SARGable 조건으로 다시 쓰기

8강에서 다룬 원칙을 튜닝 관점에서 정리합니다. 조건 열을 가공하지 않아야 인덱스 시크가 가능합니다.

-- ❌ 스캔 유발
WHERE YEAR(order_date) = 2024
WHERE CONVERT(VARCHAR, created_at, 112) = '20240101'
WHERE ISNULL(status, 'N') = 'A'

-- ✅ 시크 가능
WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02'
WHERE status = 'A'

통계(Statistics)

옵티마이저는 열의 값 분포를 담은 통계를 보고 계획을 세웁니다. 통계가 낡으면 행 수를 잘못 추정해 엉뚱한 계획을 고릅니다.

-- 통계 수동 갱신
UPDATE STATISTICS Orders;

-- 인덱스 재구성과 함께
ALTER INDEX ALL ON Orders REBUILD;

대부분 자동 갱신이 켜져 있지만, 대량 적재 직후에는 수동 갱신이 도움이 됩니다.

흔한 안티패턴

안티패턴문제개선
SELECT *불필요한 열·Key Lookup 유발필요한 열만 명시
조건 열에 함수인덱스 못 탐SARGable하게 변형
OR 남용인덱스 활용 저하UNION ALL 분리 검토
암시적 형변환타입 불일치로 스캔매개변수 타입 일치
NOT IN (NULL 포함)빈 결과NOT EXISTS
함수 안의 스칼라 함수 호출행마다 실행인라인 TVF/식
-- 암시적 형변환 예: 컬럼이 VARCHAR인데 숫자로 비교
WHERE code = 12345        -- ❌ code가 VARCHAR면 형변환→스캔
WHERE code = '12345'      -- ✅ 타입 일치

⚠️ 주의 — 추측으로 인덱스를 마구 추가하지 마세요. 먼저 실행 계획과 logical reads 로 병목을 확인하고, 인덱스 추가 전후를 측정해 효과를 검증하는 것이 정석입니다.

튜닝 절차 요약

  1. 느린 쿼리를 찾는다(STATISTICS IO/TIME, 실제 실행 계획).
  2. Scan·Key Lookup·예상/실제 행 차이 등 병목을 식별한다.
  3. 조건을 SARGable하게 고치고, 필요한 인덱스를 설계한다.
  4. 통계를 갱신하고, 변경 전후 logical reads 를 비교한다.

요약

  • 실행 계획과 STATISTICS IO(logical reads)로 병목을 측정합니다.
  • 시크는 필요한 행만, 스캔은 전체를 읽습니다. 큰 테이블의 스캔을 줄이세요.
  • 조건 열을 가공하지 않는 SARGable 작성이 인덱스 시크의 핵심입니다.
  • 통계가 낡으면 계획이 어긋나니 필요 시 갱신합니다.
  • SELECT *·함수 조건·암시적 형변환·NOT IN NULL 같은 안티패턴을 피합니다.

연습문제

  1. STATISTICS IO 를 켜고 임의 쿼리의 logical reads를 확인해 보세요.
  2. WHERE YEAR(order_date) = 2024 를 시크 가능한 조건으로 바꾸세요.
  3. Key Lookup이 잦은 쿼리를 커버링 인덱스로 개선하는 방안을 적어 보세요.
  4. 암시적 형변환이 스캔을 유발하는 예와 해결책을 작성하세요.

힌트 — 2번은 날짜 범위(>=/<), 3번은 INCLUDE 로 필요한 열을 인덱스에 담는 것입니다.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

MSSQL” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.