
윈도우 스케쥴러는 SQL 서버의 내부 동작을 파악할 수 없다. 따라서 SQL 서버 내에는 윈도우 스케쥴러가 이해하지 못하는 대기 상태가 수많이 존재한다. 때문에 SQL 서버에는 CPU 리소스를 보다 효율적으로 활용하기 위한 독자적인 스케쥴러 기능이 있다.
SQLOS 스케쥴러
- 윈도우 스케쥴러가 이해하지 못하는 대기 상태의 스레드를 컨트롤
- SQL 서버 내의 락 획득 대기 상태
- 병렬 처리로 인한 부모 스레드의 대기 상태
- SQL 서버라는 애플리케이션 내의 컴포넌트
➡️ 윈도우 스케쥴러의 지배 하에 있음
SQLOS 스케쥴러를 구성하는 컴포넌트
- 스케쥴러
- SQL 서버 내에 CPU 수 또는 코어 수와 같은 수의 스케쥴러 생성됨
- 작업자를 관리함
- 한 번에 하나의 작업자만을 액티브로 함
- 작업자
- 하나의 작업자에 하나의 스레드가 링크됨
- 클라이언트의 리퀘스트는 하나 이상의 작업자에 링크됨
- 작업자 스레드 풀
- 각 스케쥴러에서 사용 가능한 작업자를 관리하는 풀
- 최대 작업자의 수 =
max worker threads 설정값 / CPU 수
- 러너블 큐
- 각 스케쥴러는 하나의 러너블 큐를 가짐
- 둘 이상의 작업자가 실행 가능한 경우 러너블 큐에 들어감
- 러너블 큐에서 선택된 작업자는 액티브가 됨
- 워크 리퀘스트 큐
- 작업자의 수가 이미 최댓값에 달해 작업자를 생성할 수 없을 때 리퀘스트가 들어감
- 블로킹이나 과도한 병렬 처리가 원인
- I/O 리퀘스트 리스트
- I/O를 요구한 작업자는 'I/O 완료' 시점까지 I/O 리퀘스트 리스트에 들어감
- 이 때 'I/O 완료'는 OS 차원의 I/O 완료 + SQL 서버에서 이뤄지는 후처리
- 웨이터 리스트
- 작업자의 처리 실행 시에 필요한 SQL 서버 내의 리소스를 획득할 수 없는 경우 웨이터 리스트에 들어감
- 웨이터 리스트는 SQL 서버 내의 오브젝트 별로 존재함
스케쥴러의 동작
클라이언트가 테이블 X에 업데이트를 요청한 상황
다른 클라이언트가 테이블 X에 대해 락을 획득해 작업중인 상황
- 클라이언트가 SQL 서버에 접속하면 하나의 스케줄러와 링크됨
- 작업자 스레드 풀에 사용 가능한 작업자가 있는지 확인함
- 클라이언트와 작업자 A를 바인딩
- 작업자 A는 러너블 큐에 추가되어 CPU 리소스를 할당받기를 기다림
- 러너블 큐의 상위에 있는 모든 작업자가 처리된 후 작업자 A는 스케줄러의 사용권을 받음
음 ➡️ 실행 상태
- 작업자 A는 테이블 X에 대한 락이 필요한데, 이미 다른 작업자가 락을 가지고 있다. 따라서 테이블 X에 대한 락 리소스의 웨이터 리스트에 추가됨 + 작업자 A가 스케쥴러의 사용권을 러너블 큐의 다른 작업자에게 넘김 ➡️ 대기 상태
- 작업자 A가 테이블 X의 락을 획득하고 다시 러너블 큐에 추가됨
- 러너블 큐의 상위에 있는 모든 작업자가 처리된 후 작업자 A는 스케줄러의 사용권을 받음
음 ➡️ 실행 상태
- 작업자 A는 디스크 I/O가 필요하므로, I/O 리퀘스트를 수행한 후 I/O 리퀘스트 리스트에 등록됨 + 작업자 A가 스케쥴러의 사용권을 러너블 큐의 다른 작업자에게 넘김 ➡️ 대기 상태
- I/O 리퀘스트가 완료되면 작업자 A는 러너블 큐에 추가됨
- 러너블 큐에서 대기 시간이 지난 후 작업자 A는 스케줄러의 사용권을 받음 ➡️ 실행 상태
- 작업자 A는 획득한 테이블 X의 락을 해제함
- 작업자 A는 처리 결과를 클라이언트에게 회신함