SQL Server SQLOS — 운영체제 추상화 계층 완전 가이드

SQL Server SQLOS의 역할, Task·Worker·Scheduler 3계층 모델, 메모리 관리, I/O 서브시스템, NUMA 인식, 대기 통계 진단 방법을 설명합니다.

· 6 min read · PALDYN Team

지난 글에서 MariaDB의 시간 여행 쿼리를 살펴봤다. 이번 글부터는 SQL Server(MSSQL) 시리즈를 시작한다. 첫 번째 주제는 SQL Server 내부에서 운영체제 역할을 수행하는 SQLOS다.

SQLOS란

SQLOS(SQL Server Operating System)는 SQL Server 7.0부터 내장된 경량 운영체제 추상화 계층이다. Windows 커널 위에서 동작하지만 스케줄링, 메모리 관리, I/O, 동기화를 SQL Server가 직접 제어한다. SQL Server가 OS에 의존하지 않고 독자적인 자원 관리를 수행하는 이유다.

SQLOS가 관리하는 핵심 기능:

  • 스케줄러(UMS): 논리 CPU마다 스케줄러 생성, 협력적 멀티태스킹
  • 메모리: Buffer Pool, MemClerk별 메모리 청크 관리
  • I/O: Completion Port 기반 비동기 I/O 큐
  • 동기화: Latch, Spinlock, Mutex 등 내부 잠금 오브젝트

SQLOS 아키텍처

Task · Worker · Scheduler 3계층

SQLOS의 실행 모델은 세 계층으로 이루어진다.

계층설명DMV
Task쿼리 실행 단위. 병렬 쿼리면 여러 Task 생성sys.dm_os_tasks
WorkerOS 스레드를 래핑한 실행 컨텍스트sys.dm_os_workers
Scheduler논리 CPU 1:1 매핑. 한 번에 하나의 Worker만 실행sys.dm_os_schedulers

Task가 실행되려면 Worker에 배정되고, Worker는 Scheduler 위에서 실행된다.

Task·Worker·Scheduler 상태

협력적 스케줄링 (Cooperative Scheduling)

SQLOS는 선점형(preemptive)이 아닌 협력적(cooperative) 스케줄링을 기본으로 사용한다. Worker가 자발적으로 CPU를 양보해야 다음 Worker가 실행된다.

양보 시점:

  • 페이지 I/O 대기
  • 네트워크 전송 대기
  • 잠금 대기 (Lock/Latch)
  • 배치 경계(quantum 초과: 기본 4ms)
-- 스케줄러별 상태 확인
SELECT
    scheduler_id,
    cpu_id,
    status,
    is_online,
    current_tasks_count,        -- 현재 실행 중인 태스크 수
    runnable_tasks_count,       -- 실행 대기 중인 태스크 수 (높으면 CPU 병목)
    current_workers_count,
    work_queue_count,           -- 배정 대기 태스크 수
    pending_disk_io_count       -- 완료 대기 중인 디스크 I/O 수
FROM sys.dm_os_schedulers
WHERE status = 'ONLINE'
ORDER BY scheduler_id;

runnable_tasks_count가 지속적으로 0보다 크면 CPU 경합이 발생 중이라는 신호다.

메모리 관리 — MemClerk

SQLOS는 메모리를 MemClerk(Memory Clerk) 단위로 관리한다. 각 컴포넌트(Buffer Pool, 플랜 캐시, 정렬 버퍼 등)가 개별 MemClerk를 가진다.

-- 메모리 사용량 상위 컴포넌트
SELECT TOP 15
    type AS clerk_type,
    name,
    pages_kb / 1024 AS used_mb,
    virtual_memory_reserved_kb / 1024 AS vm_reserved_mb
FROM sys.dm_os_memory_clerks
ORDER BY pages_kb DESC;

-- Buffer Pool 총 크기
SELECT
    physical_memory_in_use_kb / 1024 AS physical_mb,
    page_fault_count,
    memory_utilization_percentage
FROM sys.dm_os_process_memory;

I/O 서브시스템

SQLOS는 Windows I/O Completion Port를 사용해 비동기 I/O를 처리한다. Worker가 I/O를 요청하면 즉시 SUSPENDED 상태로 전환하고 Scheduler는 다른 Worker를 실행한다. I/O가 완료되면 Completion Port가 Worker를 RUNNABLE 큐에 추가한다.

-- I/O 대기 파일별 통계
SELECT
    DB_NAME(vfs.database_id) AS db_name,
    mf.physical_name,
    vfs.io_stall_read_ms,
    vfs.io_stall_write_ms,
    vfs.num_of_reads,
    vfs.num_of_writes,
    vfs.io_stall_read_ms / NULLIF(vfs.num_of_reads,0) AS avg_read_ms,
    vfs.io_stall_write_ms / NULLIF(vfs.num_of_writes,0) AS avg_write_ms
FROM sys.dm_io_virtual_file_stats(NULL, NULL) vfs
JOIN sys.master_files mf
  ON vfs.database_id = mf.database_id
 AND vfs.file_id     = mf.file_id
ORDER BY vfs.io_stall_read_ms + vfs.io_stall_write_ms DESC;

읽기 평균 대기가 20ms, 쓰기 평균 대기가 30ms 이상이면 스토리지 성능 검토가 필요하다.

NUMA 인식

SQL Server 2005+부터 NUMA(Non-Uniform Memory Access) 토폴로지를 인식한다. 각 NUMA 노드에 독립적인 스케줄러 그룹과 Buffer Pool 파티션을 배치해 크로스 NUMA 메모리 접근을 최소화한다.

-- NUMA 노드별 스케줄러 분포
SELECT
    parent_node_id AS numa_node,
    COUNT(*) AS scheduler_count
FROM sys.dm_os_schedulers
WHERE status = 'ONLINE'
GROUP BY parent_node_id
ORDER BY parent_node_id;

-- Soft-NUMA 설정 (SQL Server 2016+, 자동 소프트 NUMA)
-- 하나의 NUMA 노드에 CPU가 8개 이상이면 자동으로 분할
SELECT name, value_in_use
FROM sys.configurations
WHERE name = 'automatic soft-NUMA disabled';

대기 통계 진단

SQL Server 성능 진단의 핵심은 **대기 통계(Wait Statistics)**다. 모든 병목은 sys.dm_os_wait_stats에 누적된다.

-- 누적 대기 통계 (인스턴스 시작 이후 전체)
SELECT TOP 20
    wait_type,
    waiting_tasks_count,
    wait_time_ms,
    max_wait_time_ms,
    signal_wait_time_ms,                        -- CPU 경합 대기
    wait_time_ms - signal_wait_time_ms AS resource_wait_ms
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (                        -- 무해한 백그라운드 대기 제외
    'SLEEP_TASK','LAZYWRITER_SLEEP',
    'SQLTRACE_BUFFER_FLUSH','CLR_AUTO_EVENT',
    'REQUEST_FOR_DEADLOCK_SEARCH','RESOURCE_QUEUE'
)
ORDER BY wait_time_ms DESC;

-- 대기 통계 초기화 (기준선 재설정)
DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

주요 대기 유형과 의미:

대기 유형원인
CXPACKET병렬 쿼리 스레드 동기화 — MAXDOP 조정 고려
LCK_M_*락 경합 — 트랜잭션 길이·인덱스 확인
PAGEIOLATCH_*버퍼 풀에 없는 페이지 I/O — 스토리지 성능 확인
WRITELOG로그 I/O 병목 — 로그 파일 디스크 분리
SOS_SCHEDULER_YIELDCPU 경합 — runnable_tasks_count 확인
RESOURCE_SEMAPHORE쿼리 메모리 그랜트 대기 — 플랜 최적화

SQLOS를 이해하면 “왜 느린가”를 추측이 아닌 데이터로 파악할 수 있다. 다음 글에서는 SQLOS 위에서 동작하는 스케줄러·워커·스레드 모델을 더 깊게 살펴본다.


지난 글: MariaDB System-Versioned Tables — 시간 여행 쿼리 완전 가이드

다음 글: SQL Server 스케줄러·워커·스레드 모델


읽어주셔서 감사합니다. 😊