2025년 12월 05일

SQL SERVER OLTP 환경에서 리소스 상태를 모니터링하고 임계치를 설정·계산하는 법

OLTP(Online Transaction Processing) 시스템은 “느낌”으로 운영하기가 어렵습니다. 짧고 잦은 트랜잭션, 높은 동시성, 그리고 응답 시간 SLA(P95/P99) 같은 명확한 목표가 있기 때문입니다.
따라서 리소스 상태(압력)를 숫자와 기준(임계치) 으로 관리할 수 있어야 합니다.

다양한 OLTP 모니터링 기둥을 나타내는 그래픽. CPU 사용량, 메모리/버퍼 풀, 디스크 I/O, tempdb 및 버전 저장소, 대기 통계를 포함하여 SLA(P95/P99 지연 시간)를 중심으로 배치되어 있음.

1. OLTP 환경에서 봐야 할 것들: 관점 정리

OLTP 워크로드의 특징은 대체로 다음과 같습니다.

  • 짧고 잦은 트랜잭션
  • 많은 동시 사용자
  • 소량의 행을 자주 읽고 쓰는 패턴
  • 대개 “응답 시간 SLA”가 존재 (예: API P95 < 100ms)

이 특성을 고려하면, 모니터링 포인트는 자연스럽게 아래 5개 축으로 수렴합니다.

1) CPU 사용량 + Signal Wait 비율(스케줄러/CPU 압력)
2) 메모리/버퍼 풀 상태(PLE, 읽기 증가, 캐시 효율)
3) 디스크 I/O 성능(파일별 지연, I/O 관련 Wait)
4) tempdb & Version Store(스냅샷 격리/ONLINE 인덱스/정렬·해시 등)
5) Wait Statistics(“어디서 무엇 때문에 기다리는지”의 최종 요약 지표)

핵심은 단순히 “지표를 보는 것”이 아니라, 위 5개 축에서 Baseline(정상 패턴) 을 만들고 각 축별 임계치(경고/심각)를 수치화하는 것입니다.
이렇게 하면 OLTP 시스템의 “건강 상태”를 운영 관점에서 일관되게 관리할 수 있습니다.


2. CPU: 사용률과 Signal Wait로 보는 압력

2.1 무엇을 볼 것인가

OS/PerfMon(또는 모니터링 도구)

  • Processor: % Processor Time
  • System: Processor Queue Length
  • 해석 시 “절대값”보다 코어 수 대비 큐 길이, 그리고 지속 시간이 중요합니다.

SQL Server 내부

  • sys.dm_os_wait_stats에서 CPU 관련 Wait
  • SOS_SCHEDULER_YIELD(스케줄러 양보 → CPU 경쟁/압력 신호)
  • THREADPOOL(워크 스레드 부족)
  • 요청/쿼리 당 CPU 시간
  • Query Store 또는 sys.dm_exec_query_stats 기반으로 “CPU 먹는 상위 쿼리” 확인
Illustration depicting CPU pressure in SQL Server, showing a CPU, scheduler/queue, worker threads, and indicators of high CPU percentage and high signal wait percentage leading to scheduler contention.
Diagram illustrating CPU pressure in SQL Server, showing high CPU and high signal wait percentages leading to scheduler contention and worker threads waiting to run.

2.2 간단 모니터링 스크립트(Wait 기반)

아래는 Wait Stats에서 “CPU 압력 후보”를 빠르게 보는 용도로 유용합니다.

주의: sys.dm_os_wait_statsSQL Server 시작 이후 누적값입니다.
운영 분석에서는 “현재 값”을 그대로 보지 말고, 일정 간격으로 샘플링해서 Δ(증분, delta) 로 보는 방식이 가장 안전합니다.

-- CPU 관련 Wait 관찰(누적 기반)
SELECT TOP 10
       wait_type,
       wait_time_ms,
       signal_wait_time_ms,
       CAST(100.0 * signal_wait_time_ms / NULLIF(wait_time_ms, 0) AS decimal(10,2)) AS signal_wait_pct,
       waiting_tasks_count
FROM sys.dm_os_wait_stats
WHERE wait_type NOT LIKE 'SLEEP%'
ORDER BY wait_time_ms DESC;

Signal Wait 비율이 높다는 것은 “대기 중인 스레드가 CPU를 쓰기 위해 스케줄링되길 기다리는 시간”이 크다는 뜻입니다.
즉, 단순 CPU%뿐 아니라 스케줄러 대기(Signal Wait) 가 같이 상승하면 CPU 압력 가능성이 높습니다.


2.3 CPU 임계치 설정 예시(Baseline 기반)

CPU 임계치는 “몇 %가 절대적으로 위험”이라기보다, 내 시스템의 정상 패턴 대비 얼마나 튀는가로 잡는 편이 현실적입니다.

권장 접근:

1) 1~2주 정도 정상/피크 상황 데이터를 수집
2) 그 중 “서비스 품질이 만족스러운 구간(SLA 만족 구간)”만 필터링
3) 그 구간에서 CPU 평균/분포를 계산(예: 5분 평균, P95)

예시(가정):

  • SLA 만족 구간에서 CPU 5분 평균 = 35%
  • SLA 만족 구간에서 CPU P95 = 60%

임계치(예시):

  • 경고: CPU 5분 평균 > 70%
  • 심각: CPU 5분 평균 > 85%가 10분 이상 지속
  • 보조 신호: SOS_SCHEDULER_YIELD 증가 + signal wait 비율 상승
A graph illustrating threshold monitoring in an OLTP environment, showing critical and warning thresholds along with a baseline zone and a 5-minute average.

3. 메모리 & 버퍼 풀: PLE 중심으로 “압력”을 읽기

3.1 주요 지표

PerfMon: SQLServer:Buffer Manager

  • Page life expectancy (PLE)
  • Page reads/sec, Page writes/sec

기본 개념:

  • PLE는 “페이지가 버퍼 풀(메모리)에 얼마나 오래 머무는지(초)”를 나타냅니다.
  • 과거에는 “PLE < 300이면 문제” 같은 단일 규칙이 많이 쓰였지만,
    실제로는 버퍼 풀 크기/워크로드에 따라 다르게 해석해야 합니다.
  • 또한 PLE는 NUMA/Buffer Node 단위로 나뉘어 보이는 환경이 있어, 가능하면 인스턴스/노드별 추세를 같이 보는 편이 좋습니다.
Diagram illustrating Page Life Expectancy (PLE) in relation to buffer pool management, showing how increased physical reads and low PLE lead to higher churn.

3.2 PLE 임계치(룰 오브 썸, 참고) 계산 예시

실무에서 자주 참고하는 경험 공식(룰 오브 썸) 중 하나는:

  • PLE 임계치(초) ≈ (버퍼 풀 메모리(GB) / 4) × 300

예를 들어 버퍼 풀 메모리 = 32GB라면:

  • 32 / 4 = 8
  • 8 × 300 = 2400초

해석:

  • PLE가 2400초 근처라면 대체로 정상 범주로 볼 여지가 있고,
  • 그보다 훨씬 낮게 떨어지는 구간이 길게 유지된다면 메모리 압력 또는 비효율적인 쿼리(대량 스캔 등) 를 의심할 수 있습니다.

주의: 이 공식은 “절대 정답”이 아니라, Baseline 설계를 위한 출발점으로 사용하고
실제 운영에서는 SLA 만족 구간에서 관측된 PLE 분포를 기준으로 보정하는 편이 더 안전합니다.


3.3 임계치 설계 예시

  • 경고: PLE < (계산값의 50%) 상태가 10분 이상 지속
  • 심각: PLE < (계산값의 30%) 상태가 10분 이상 지속 + Page reads/sec가 평소 대비 2배 이상 증가

포인트는 “PLE 하락”이 단독으로 문제라기보다, 물리 읽기(Page reads/sec) 증가와 같이 나타나는지 확인하는 것입니다.


4. 디스크 I/O: 지연(Latency)과 Wait로 병목 찾기

4.1 무엇을 볼 것인가

Windows PerfMon

  • PhysicalDisk: Avg. Disk sec/Read
  • PhysicalDisk: Avg. Disk sec/Write

SQL Server DMV

  • sys.dm_io_virtual_file_stats로 데이터/로그 파일별 평균 지연(ms) 관찰
  • I/O 관련 Wait
  • PAGEIOLATCH_*(데이터 페이지 읽기 대기)
  • WRITELOG(로그 flush 대기)
  • ASYNC_IO_COMPLETION

일반적으로 현업에서는 평균 지연을 다음처럼 “대략적인 체감 범주”로 분류하는 경우가 많습니다(워크로드/스토리지 특성에 따라 다름).

  • < 5ms: 매우 우수
  • 5~10ms: 양호
  • 10~20ms: 주의
  • > 20ms: 병목 가능성 높음(지속 시 원인 분석 필요)
A graphical representation highlighting SQL Server disk/file latency, showing average read and write times with corresponding gauges. Also lists common I/O wait types.

4.2 파일별 평균 지연 확인 스크립트(개선 버전)

SELECT
    DB_NAME(vfs.database_id) AS [Database],
    mf.name                  AS [LogicalName],
    mf.type_desc             AS [Type],
    vfs.num_of_reads,
    vfs.io_stall_read_ms,
    CAST(1.0 * vfs.io_stall_read_ms / NULLIF(vfs.num_of_reads, 0) AS decimal(10,2)) AS [AvgRead_ms],
    vfs.num_of_writes,
    vfs.io_stall_write_ms,
    CAST(1.0 * vfs.io_stall_write_ms / NULLIF(vfs.num_of_writes, 0) AS decimal(10,2)) AS [AvgWrite_ms],
    mf.physical_name
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
JOIN sys.master_files AS mf
  ON vfs.database_id = mf.database_id
 AND vfs.file_id     = mf.file_id
ORDER BY [AvgRead_ms] DESC;

주의: 이것도 “누적 평균” 성격이 강합니다.
운영에서는 일정 간격으로 샘플링해 구간 평균/증분 기반 지연을 보는 방식이 더 정확합니다.


4.3 임계치 설계 예시

  • 경고: 데이터 파일 AvgRead_ms 또는 AvgWrite_ms > 15ms가 10분 이상 지속
  • 심각: > 30ms가 지속 + I/O 관련 Wait(PAGEIOLATCH_*, WRITELOG)가 Top Wait 상위에 고착

I/O는 스토리지/아키텍처에 따라 정상 범위가 달라지므로, 최종 임계치는 반드시 Baseline + SLA 만족 구간을 기준으로 보정하는 것을 권장합니다.


5. tempdb & Version Store: ONLINE 인덱스/스냅샷의 숨은 비용

tempdb는 단순히 “임시 테이블 쓰는 곳”이 아닙니다.

  • 정렬/해시 연산, 임시 객체
  • 스냅샷 격리(READ_COMMITTED_SNAPSHOT, ALLOW_SNAPSHOT_ISOLATION)
  • ONLINE 인덱스 작업의 버전 스토어(version store)

특히 OLTP에서 ONLINE 인덱스 작업, 스냅샷 격리를 함께 사용한다면 version store가 tempdb를 빠르게 잠식할 수 있습니다.

A chart depicting the breakdown of tempdb space usage in SQL Server, showing categories such as Free Space, Version Store, Internal Objects, and User Objects along with a Growth Rate indicator.

5.1 Version Store 모니터링(대표 DMV)

-- DB별 Version Store 사용량 (KB)
SELECT
    DB_NAME(database_id) AS [Database],
    reserved_page_count * 8 AS reserved_kb
FROM sys.dm_tran_version_store_space_usage
ORDER BY reserved_kb DESC;
-- tempdb 파일별 공간 사용량 및 Version Store
USE tempdb;
GO

SELECT
    SUM(user_object_reserved_page_count)     * 8 AS user_kb,
    SUM(internal_object_reserved_page_count) * 8 AS internal_kb,
    SUM(version_store_reserved_page_count)   * 8 AS version_store_kb,
    SUM(unallocated_extent_page_count)       * 8 AS free_kb
FROM sys.dm_db_file_space_usage;

실무에서는 위 값을 1분 간격으로 샘플링해서 증가 속도(Δversion_store_kb/분) 를 함께 보는 편이 효과적입니다.


5.2 임계치 설계 예시(보수적으로)

  • tempdb 전체 크기 대비 Version Store 비율 기준
  • 경고: Version Store가 tempdb의 20% 이상
  • 심각: 30% 이상 + 증가 속도가 빠르게 유지
  • 증가 속도 기준
  • 1분 간격 샘플링으로 Δversion_store_kb/분 계산
  • baseline 대비 2~3배 이상이면 주의

tempdb는 장애를 막는 마지막 방어선이 되는 경우가 많으므로, 임계치를 조금 더 보수적으로 잡는 것이 안전합니다.


6. Wait Statistics: “어디서 기다리는지”로 병목 찾기

대부분의 튜닝 가이드는 Wait Stats를 출발점으로 삼으라고 권합니다. 이유는 단순합니다.

  • 최종적으로 “CPU/IO/Lock/Network/Memory” 중 어디에서 시간이 소모되는지
  • 가장 먼저 한 눈에 요약해 주기 때문입니다.

대표 Wait 유형 예시:

  • CPU: SOS_SCHEDULER_YIELD, THREADPOOL
  • I/O: PAGEIOLATCH_*, WRITELOG, ASYNC_IO_COMPLETION
  • 락/동시성: LCK_M_*, LATCH_*
  • 네트워크/클라이언트: ASYNC_NETWORK_IO

상위 Wait 조회(기본 형태):

WITH Waits AS
(
    SELECT
        wait_type,
        wait_time_ms        = wait_time_ms - signal_wait_time_ms,
        signal_wait_time_ms = signal_wait_time_ms,
        waiting_tasks_count
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT LIKE 'SLEEP%'
)
SELECT TOP 15
       wait_type,
       wait_time_ms,
       signal_wait_time_ms,
       waiting_tasks_count
FROM Waits
ORDER BY wait_time_ms DESC;

임계치를 “wait_time_ms가 얼마 이상이면 문제”처럼 고정하기보다는,

  • 평소 정상 구간에서 Top N Wait 패턴을 저장해 두고
  • Top N Wait 이름이 바뀌었는지, 비율이 얼마나 튀었는지
  • 즉, “이상 탐지”에 가깝게 운영하는 것이 현실적입니다.
A chart titled 'Wait Statistics Map' categorizing different types of wait statistics in a SQL Server environment, including CPU, I/O, Lock/Concurrency, Network, and tempdb categories, each with associated wait types listed.

7. 임계치 설정/계산 방법론: Baseline + 비즈니스 SLA

7.1 단계 1 – Baseline 수집

1~2주 이상, 일정 간격(예: 1분/5분)으로 다음 데이터를 수집합니다.

  • PerfMon/모니터링 도구: CPU, 메모리, 디스크, 네트워크, tempdb 사용량
  • SQL DMVs: Wait Stats, IO Stats, tempdb/version store, Top Queries(쿼리 CPU/Duration 등)

그중 SLA가 만족되는 시간대만 필터링해서 각 지표의 평균/표준편차/P95를 계산합니다.


7.2 단계 2 – 리소스 임계치와 SLA 연결

예를 들어 SLA가 “웹 API P95 응답 시간 < 100ms”라고 가정합니다.
실제 데이터에서 SLA 만족 구간의 리소스가 아래였다면:

  • CPU 5분 평균 40%, P95 60%
  • I/O 평균 8ms, P95 12ms
  • PLE 평균 3000초
  • tempdb version store < 10%

임계치를 다음처럼 역산할 수 있습니다(예시):

  • CPU 경고: 60% + 여유 10% → 70%
  • I/O 경고/심각: 15ms / 30ms
  • PLE 경고/심각: 관측 최소값 기준으로 하향 설정
  • tempdb version store: 20% / 30% + 증가 속도 조건

즉, 임계치의 출발점은 “남들이 말하는 숫자”가 아니라
우리 시스템이 SLA를 만족할 때의 리소스 레벨이어야 합니다.

A flowchart illustrating the SLA-based monitoring workflow, featuring steps such as collecting baseline metrics, filtering SLA-good windows, computing percentiles (P95), defining warning and critical thresholds, and monitoring and alerting.

7.3 계산 예시 – tempdb Version Store

가정:

  • Baseline 동안 tempdb 전체 크기 = 100GB
  • SLA 만족 구간에서 Version Store 최대 = 10GB(10%)

임계치 설계(예시):

  • 경고: 20GB(20%)
  • 심각: 30GB(30%) + 증가 속도 > (baseline 최대 증가 속도의 2배)

이렇게 하면 ONLINE 인덱스 작업처럼 version store를 급격히 사용하는 작업의 영향도를 “수치로” 감지할 수 있습니다.


8. ONLINE = ON 인덱스 작업 시 영향도 체크(OLTP 관점)

이제 OLTP 운영에서 자주 고민하는 주제를 정리합니다.

“ONLINE = ON으로 인덱스를 만들거나 리빌드할 때, 서비스에 어떤 영향을 주며, 무엇을 보고 중단/계속을 결정해야 하는가?”

8.1 ONLINE 인덱스 빌드 개요

사용 구문:

  • CREATE INDEX ... WITH (ONLINE = ON)
  • ALTER INDEX ... REBUILD WITH (ONLINE = ON)

특징(요약):

  • 인덱스 작업 중에도 대부분의 읽기/쓰기 작업이 계속 가능
  • 다만 버전 스토어(version store) 를 사용해 동시성을 확보하는 경우가 많아 → tempdb 부하 증가
  • 시작/마무리 단계에서 짧게라도 스키마 락(SCH-M 등) 을 잡을 수 있음
  • 지원 범위는 SQL Server 버전/에디션/옵션에 따라 달라질 수 있으므로, 반드시 사전 확인 필요

8.2 사전 점검 체크리스트(최소)

(1) 에디션 확인

SELECT SERVERPROPERTY('Edition') AS Edition;

(2) CPU 여유

  • 최근 피크 시간대 CPU 평균이 충분히 낮은지(예: 60% 미만 등)
  • SOS_SCHEDULER_YIELD, THREADPOOL이 상위로 치고 올라오지 않는지

(3) tempdb 여유 및 I/O 성능

USE tempdb;
GO

SELECT
    SUM(size) * 8 / 1024 AS size_mb
FROM sys.database_files;

SELECT
    SUM(unallocated_extent_page_count)     * 8 / 1024 AS free_mb,
    SUM(version_store_reserved_page_count) * 8 / 1024 AS version_store_mb
FROM sys.dm_db_file_space_usage;

(4) 트랜잭션 로그 여유

  • ONLINE 인덱스 작업은 로그도 많이 사용합니다.
  • 로그 파일 크기/자동 증가/디스크 여유를 반드시 확인합니다.

8.3 ONLINE 인덱스 생성 예시(OLTP 친화적 옵션 예)

USE YourOLTPDB;
GO

CREATE NONCLUSTERED INDEX IX_Orders_CustomerId_OrderDate
ON dbo.Orders (CustomerId, OrderDate)
INCLUDE (Amount)
WITH
(
    ONLINE = ON,
    SORT_IN_TEMPDB = ON,
    MAXDOP = 2
);
GO

운영 팁: OLTP에서는 인덱스 작업이 전체 CPU를 잠식하지 않도록 MAXDOP를 보수적으로 두는 편이 안전합니다.


8.4 실행 중 영향도 모니터링(핵심 3가지)

(1) 현재 인덱스 작업 진행 상태/대기 유형

SELECT
    r.session_id,
    r.command,
    r.status,
    r.percent_complete,
    r.cpu_time,
    r.total_elapsed_time,
    r.wait_type,
    r.wait_time,
    r.wait_resource,
    t.text
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.command LIKE '%INDEX%'
  AND r.database_id = DB_ID('YourOLTPDB');
  • percent_complete로 진행률/예상 소요를 가늠
  • wait_type으로 병목이 CPU/I/O/락/tempdb 어디인지 판단

(2) tempdb Version Store 증가 속도

USE tempdb;
GO

SELECT
    GETDATE() AS sample_time,
    SUM(version_store_reserved_page_count) * 8 / 1024 AS version_store_mb
FROM sys.dm_db_file_space_usage;

이 값을 1분 간격으로 수집하면, ONLINE 인덱스가 version store를 얼마나 빠르게 채우는지 추적 가능합니다.

(3) OLTP 응답 시간·처리량(SLA)

  • APM/모니터링에서:
  • API 응답 시간(P95/P99)
  • TPS
  • 타임아웃/에러 비율
  • “절대값”보다 Baseline 대비 악화율(%) 을 함께 보는 것이 핵심입니다.
Monitoring ONLINE Index Operation Impact, including percent complete (63%), tempdb Version Store Growth, and performance indicators for warning and critical levels.

8.5 임계치 기반 “중단/계속” 의사결정 예시

사전 정의 임계치(예시):

  • CPU 5분 평균 > 80%(또는 baseline 대비 +30% 이상)
  • tempdb Version Store > tempdb의 30%
  • API P95 응답 시간 > 2 × baseline(예: 100ms → 200ms 이상)
  • I/O 관련 Wait(PAGEIOLATCH_*, WRITELOG)가 Top 1~2를 장시간 유지

운영 정책(예시):

  • 위 항목 중 2개 이상이 5분 이상 지속 → 인덱스 작업 중단(KILL) 및 재스케줄
  • 하나만 위배되더라도 SLA 지표(API 지연/타임아웃)가 명확히 악화되면 즉시 중단
  • 지원 환경이라면 Resumable(RESUMABLE=ON) 옵션을 활용해 “중단 후 재개” 전략을 고려(버전/에디션 지원 여부 확인)

9. OLTP 리소스 모니터링 & 임계치 체크리스트(실무 요약)

9.1 모니터링 항목(최소 세트)

  • CPU: 5분 평균, Signal Wait 비율
  • 메모리: PLE, Page reads/sec, 캐시 효율(가능 시)
  • 디스크 I/O: Avg. Disk sec/Read/Write, 파일별 AvgRead/AvgWrite(ms)
  • tempdb: 공간 사용량, Version Store 크기 및 증가 속도
  • Wait Stats: Top N Wait 유형과 비율 변화
  • SLA 지표: API P95/P99, TPS, 타임아웃/에러 비율

9.2 임계치 설계 원칙

  • 최소 1~2주 Baseline 수집
  • SLA 만족 구간에서 평균/P95/최소값 계산
  • “경고” = Baseline 상한 + 여유(예: +20%)
  • “심각” = Baseline 상한 + 더 큰 여유(예: +40%) 또는 SLA 직접 위배
  • 반드시 지속 시간(Duration)증가 속도(Rate) 조건을 함께 둠

9.3 ONLINE 인덱스 작업 운영 원칙

  • Edition/버전/지원 옵션 확인
  • 작업 전 CPU/tempdb/로그/I/O 여유 확인
  • 작업 중:
  • sys.dm_exec_requests로 진행률·Wait 유형 모니터링
  • tempdb version store 증가량 추적
  • SLA 지표(응답 시간/오류) 실시간 체크
  • 임계치 초과 시 중단/재조정 원칙 준수(가능하면 resumable 전략 포함)
A checklist for monitoring and setting thresholds in an OLTP environment, featuring icons for CPU, Memory, Disk I/O, tempdb, Wait Stats, and SLA metrics, along with decision rules regarding duration and rate of change.