PracticeEveryday

그림으로 이해하는 SQL 서버의구조 본문

그림으로 이해하는 SQL 서버의구조

kimddakki 2022. 7. 16. 20:42
CPU 리소스의 최적화

 - 이 장에서는 SQL 서버가 CPU 리소스를 효율적으로 사용하기 위해 어떻게 고안되어 있는지에 대해 소개한다.

 

1.1 멀티 스레드 프로그래밍

※ 스레드 : 프로그램 내의 처리 실행 단위 혹은 CPU의 이용 단위를 나타내는 단어

※ 싱글 스레드 프로그래밍 : 일련의 처리를 단일 스레드만으로 직렬 처리하는 프로그래밍 방법

※ 멀디 스레드 프로그래밍 : 동일 어드레스 공간의 메모리를 공유하면서 병렬로 처리하는 방법

※ 멀티 태스크 처리 : 다른 애플리케이션 ( 또는 프로세스 ) 의 처리를 다른 CPU에 할당함으로써 복수의 애플리케이션을

    효율적으로 처리할 수 있는 구조

※ 멀티 스레드 처리 : 병렬 처리의 효율을 높이기 위해 하나의 애플리케이션 내의 조작을 복수의 스레드로 분할하고

    각각의 스레드에 대해 다른 CPU를 할당해서 각 스레드를 병렬로 처리함으로써 처리 효율을 높이는 구조

 

최초의 윈도우 NT 버전 SQL 서버가 Microsoft SQL 서버라는 이름으로 발매된 무렵 상용 환경에서 사용하는
OS의 주류 중 하나였던 UNIX 오퍼레이팅 시스템에는 멀티 테스크 처리에는 대응했지만 멀티 스레드 처리에는
대응하지 않은 것이 많았다.
 => Microsoft OS에는 멀티 스레드 스케줄 관리 기능이 구현되어 있었다.
문맥 교환

 - 멀티 스레드 OS의 CPU상에서 실행되는 처리가 대기 상태가 되거나 타임 슬라이스 상한으로 설정된 임계값에 달함에

   따라 CPU 사용권을 양보하고 다른 처리가 CPU로 실행되는 것을 문맥 교환이라고 한다.

 => 어떤 원인( Ex I/O 대기 등 )에 의해 어느 스레드가 CPU의 사용을 정지한 경우에는 그 동안 다른 처리에 CPU 사용권을

      할당 할 수 있기 떄문에 컴퓨터 전체의 스루풋은 향상된다.

※ 타임 슬라이스 : 1회당 할당되는 CPU의 사용 가능한 시간 단위

※ 윈도우 스케줄러 : 윈도우에서 일련의 CPU 리소스 관리를 담당하고 있는 컴포넌트

윈도우 스케줄러와 SQL 서버의 동작

 - SQL 서버는 멀티 스레드로 동작하는 애플리케이션이다.

 - SQL 서버가 동작하면 하나의 인스턴스 별로 하나의 프로세스가 윈도우 상에서 생성된다.

 - SQL 서버 프로세스는 다양한 작업 ( Ex 쿼리의 실행, 백그라운드 처리 )을 처리하기 위해 내부적으로 많은 스레드를

   생성한다.

 => SQL 서버 프로세스에 의해 내부적으로 생성된 모든 스레드는 윈도우 스케쥴러에의해 CPU 리소스의 사용 상황이

      관리된다. 

  => 즉 SQL 서버 이외의 프로세스, 가령 우리의 프로그램이 생성한 프로세스에 의해서 작성된 스레드와 마찬가지 취급을

       받는 것이다. SQL 서버가 생성한 스레드도 윈도우 스케쥴러로 관리된다!

 

○ 여기서 SQL 서버가 작성한 각 스레드의 문맥 교환 발생 타이밍의 타당성을 생각해보자

1. 타임 슬라이스

 - 사용 가능한 시간을 구분해서 CPU 리소스를 효율적으로 각 스레드 간에 공유하는 룰은 SQL 서버 프로세스 내의 

   스레드 처리 내용에 아무런 악영향을 미치지 않으며 당연히 적용 되어야 한다.

2. 문맥 교환

2 - 1.  SQL 서버 프로세스 내의 스레드가 윈도우 스케줄러가 판단 가능한 대기 상태 ( I / O 대기 등 )가 된 결과, 문맥 교환이 발생하는 점은 아무 문제 없다.

 

2 - 2 SQL 서버 프로세스 내의 스레드가 윈도우 스케줄러가 판단할 수 없는 대기 상태가 된 경우가 문제가 된다.

 

윈도우 스케줄러가 인식할 수 없는 대기 상태

SQL 서버와 기타 데이터베이스 관리 시스템에서는 저장한 데이터의 일관성을 유지하기 위해 락 기능을 갖고 있다.
 => 이미 다른 쿼리가 같은 데이터에 락을 획득한 경우 그것이 헤제되기까지 기다려야 한다
 
 하지만 윈도우 스케줄러는 락 해제 대기 상태에 있는 스레드의 상태를 판단할 수가 없다.
  데이터베이스 내의 오브젝트에 대한 락 개념은 어디까지나 SQL 서버 내에 한정된 것이기 때문이다.
  때문에 윈도우 스케줄러는 락 획득 대기 상태의 스레드도, 락 획득 완료 스레드도 같은 상태라고 판단한다.
  => 따라서 윈도우 스케줄러에 모든 스레드 관리를 맡기면 락 획득 대기 스레드가 락 획득 완료 스레드보다 먼저 CPU
     리소스 사용권을 할당받게 되는 일이 벌어진다. 락 획득 대기 스레드가 CPU 리소스를 할당받아도 아무것도
     처리할 수가 없다. 그 결과 CPU 리소스가 낭비된다
SQL 서버 내의 스케줄러

 - SQL 서버가 보다 큰 사이즈의 데이터베이스를 관리하고 보다 많은 클라이언트의 리퀘스트를 효율적으로 처리하기 

   위해서는 모든 스레드의 스케줄러 관리를 윈도우 스케줄러에게만 맡길 게 아니라 독자의 컴포넌트를 준비하는 편이 

   좋다는 판단을 내렸다.

 => SQLOS 스케줄러

SQL 서버는 어디까지나 SQL 서버라는 애플리케이션 내의 컴포넌트이다.
SQL 서버는 윈도우 하에서 동작하는 애플리케이션이므로 여전히 윈도우 스케줄러의 지배하에 있다.
윈도우 스케줄러의 지배하에 있으면서 효율적으로 CPU 리소스를 사용케 하는 장치가 SQLOS 스케줄러이다!

 

SQLOS 스케줄러를 구성하는 컴포넌트

1. 스케줄러

 - SQL 서버의 동작 시에 CPU 수 또는 코어 수와 같은 수의 스케줄러가 작성된다.

 - 스케줄러는 작업자의 관리를 수행한다. 또한 스케줄러는 한 번에 하나의 작업자만을 CPU 리소스 할당이 가능한

   상태로 한다.

 

2. 작업자

 - 작업자는 SQL 서버에서 태스크를 실행하기 위해 반드시 필요한 컴포넌트이다.

 - 클라이언트로부터의 리퀘스트는 다양한 단계를 거친 후 최종적으로 하나 이상의 작업자에 링크되어 처리되고

   작업자는 SQL 서버 내의 오브젝트이지만 윈도우의 관리 오브젝트인 스레드와 링크되어 있다.

 - 하나의 작업자에 대해 반드시 윈도우 스레드가 하나 존재한다.

SQLOS 스케줄러는 비선점형 ( Non-preemptive )

비선점형 : OS는 CPU 리소스의 사용권을 관리하지 않는다. CPU의 사용권은 각 애플리케이션이 다른 애플리케이션으로
           자발적으로 양보할 필요가 있다.
     => OS가 CPU 리소스 관리를 수행하지 않는 만큼 비용을 줄일 수 있다. 하지만 CPU 리소스를 계속해서 점유하는 
        애플리케이션이 있을 경우 시스템 전체에 악영향을 미칠 수 있다.
        
선점형 : 타임 슬라이스 등의 룰에 기초해서 OS가 각 애플리케이션의 CPU 리소스 사용을 관리한다.
         ( 일정 시간이 경과하면 다른 애플리케이션으로 사용권을 양보한다. )
     => 애플리케이션의 동작에 의존하지 않고 OS가 CPU 리소스의 사용권을 관리하기 떄문에 모든 애플리케이션에서
        안정적으로 CPU 리소스를 할당할 수 있다. 그런 만큼 OS 구현이 복잡해지고 비용이 높아진다.
        예전에는 문제라고 여겼던 OS가 CPU에 미치는 부하도 오늘날의 CPU 성능의 향상으로 거의 무시할 수 있는 수준이 되었다.
        때문에 주요한 OS는 모두 선점형 방식을 채용하고 있다.
        
 
 SQLOS 스케줄러는 비선점형 방식이 도입되었다.
 일단 SQLOS 스케줄러 상에서 실행 상태가 된 스레드는 스스로 양보하기 전까지 스케줄러의 사용권을 거론할 수는 없다.
 스케줄러의 사용권을 갖는다는 것은 CPU 사용권을 갖는다는 것과 같다는 뜻인데 처리에 필요한 모든 리소스를
 획득한 스레드에 대해 가능한한 CPU를 할당하여 CPU 가동률을 최대화하려는 데 기인한다.
  => 그렇다고해도 하나의 스레드가 사용권을 유지하면 당연히 SQL 서버 전체의 스루풋은 저하된다.
     때문에 길어도 각 스레드는 수밀리초 동안 스케줄러를 점유한 후에는 다른 스레드에 사용권을 양보하도록 코딩되어 있다.
     수밀리초는 일상 속에서는 매우 짧아 수행하기 적절하지 않다 느낄 수 있지만 CPU 이용 시간이라는 점에서는
     통합된 단위를 작업 완료하는 데 문제 없다.
     이 작업 단위별로 스레드 간에서 CPU 사용권을 주고 받음으로써 CPU를 쓸데 없이 대기 시키는 기회를 줄여 시스템의 스루풋을 높인다.
     
또 한 가지 잊어서는 안 되는 것이 SQLOS 스케줄러의 관리 범위 내에서 비선점형이라고 해도 SQL 서버 자체가 윈도우하에서
동작하는 애플리케이션이라는 점이다.
 => 윈도우 스케줄러의 관점에서 본 경우는 SQLOS 스케줄러 배하의 모든 스레드는 선점형으로 동작하고 있음을 의미한다.

 

3. 작업 스레드 풀

 - 작업 스레드 풀은 각 스케줄러에서 사용 가능한 작업자의 수를 관리한다.

 - 작업자의 최대는 max worker threads의 설정값에 의존한다. 디폴트 값은 255이다.

 - 컴퓨터에 2개 이상의 CPU 코어가 탑재되어 있는 경우 각 스케줄러가 사용할 수 있는 작업자 수는 max worker threads의

   설정값을 스케줄러 수로 나눈 값이 된다.

 

4. 러너블 큐 ( Runnable Queue )

 - 각 스케줄러는 하나의 러너블 큐를 갖고 있다.

 - 앞서 말한 것처럼 스케줄러는 하나의 작업자만을 액티브로 한다. 만약 둘 이상의 작업자가 실행 가능한 경우는 러너블

   큐에 리스트되어 스케줄러가 사용 가증하기를 기다린다.

 

5. 워크 리퀘스트 큐

 - 각 스케줄러는 작업을 실행하기 위한 작업자를 작업자 스레드 풀에 보관하고 있다.

 - 그러나 많은 처리 리퀘스트가 발생하면 드물게 작업자의 수가 부족할 때가 있다.

 - 작업자가 부족해 처리를 실행하지 못하는 태스크는 워크 리퀘스트 큐에 리스트되어 작업자가 사용 가능해 질 때까지

   기다린다.

 - 작업자가 부족한 일반적인 원인은 블로킹이나 과도한 병렬처리이다.

 

6. I / O 리퀘스트 리스트

 - I / O 리퀘스트를 발행한 작업자는 요구한 I / O가 완료되기까지 I / O 리퀘스트 리스트에 추가된다.

 

7. 웨이터 리스트

 - 작업자의 처리 실행 시에 필요한 SQL 서버 내의 리소스 ( 락이나 래치 )를 획득할 수 없는 경우에 작업자는 웨이터 

   리스트에 추가된다. 

 - 웨이터 리스트는 다른 큐나 리스트와 달리 스케줄러가 직접 관리하지 않는다. 웨이터 리스트는 SQL 서버 내의 오브젝트

   별로 존재한다.

Ex ) 테이블 A의 어느 행에 대해 복수의 작업자가 엑세스해서 락을 경쟁하는 상황이 발생한 경우 웨이터 리스트에 들어가

       대기하게 되고 락이 불필요해진 시점에 락을 해제하는 동시에 웨이터 리스트 앞에 있는 작업자에게 락을 양보한다.

 

스케줄러의 동작

1. 클라이언트가 SQL 서버에 접속하면 어느 하나의 스케줄러와 링크 된다.

2. 클라이언트는 처리를 실행하기 위해 작업자가 필요하다.

    작업자 스루풋에 사용 가능한 작업자가 존재하는지를 확인한다.

    작업자가 있다면 클라이언트와 작업자를 바인드 한다.

3. 처리를 실행하기 위해서는 CPU를 할당받아야 하기 때문에 작업자A는 러너블 큐에 들어간다.

4. 러너블 큐의 상위에 있는 모든 작업자가 처리될 때까지 대기한다.

    모든 작업자가 처리되고 난 후 작업자 A는 실행 상태가 되고 실행 중이라는 정보가 스케줄러에 보관된다.

5. 작업자 A가 처리를 진행하려면 테이블 X 전체에 배타락이 필요해진다.

   하지만 이미 다른 작업자가 테이블 X에 대한 락을 획득한 상태이다.

   따라서 작업자A는 테이블 X에 대한 락 리소스의 웨이터 리스트에 추가된다.

   또 스케줄러의 사용권을 러너블 큐의 선두 작업자에게 넘기고 자신 ( 작업자 A )는 대기 상태가 된다.

6. 일정 시간 경과 후 테이블 X의 배타 락을 획득한 작업자 A는 다시 러너블 큐에 추가된다.

7 상위에 있던 작업자의 처리가 완료가 될 때까지 대기 한 후, 작업자 A는 다시 실행 상태가 된다.

 => 처리에 필요한 모든 리소스의 획득이 완료되면 CPU를 사용하기 위해 러너블 큐에서 대기한다 

8. 작업자 A는 데이터 취득을 위해 디스크에 대한 I / O가 필요하다.

    때문에 I / O 리퀘스트를 수행한다.

 => 처리에 필요한 리소스가 발생하면 그것을 획득하기 위한 대가가 발생한다. 이번의 경우는 I / O 조작 대가이다.

9 작업자 A는 I / O 리퀘스트 리스트에 등록된다.

   그리고 스케줄러 사용권을 러너블 큐의 선두 작업자에게 넘긴다.

   자신은 대기 상태가 된다.

10 일정 시간 경과 후 I / O 리퀘스트가 완료된다.

     작업자 A는 처리를 계속하기 위해 러너블 큐로 이동한다.

 => 다시 처리에 필요한 리소스를 획득했기 때문에 러너블 큐에서 대기한다.

11 러너블 큐에서 대기 시간이 지난 후 작업자 A는 실행 상태가 된다.

     필요한 처리를 계속한다.

 => 스케줄러의 사용권을 얻어 CPU 처리를 진행한다.

12 작업자 A는 획득한 테이블 X의 락을해제하고 웨이터 리스트의 선두 작업자에 획득권을 건넨다.

 => 스케줄러의 사용권을 얻어 CPU로 처리를 진행한다.

13 작업자 A는 처리 결과를 클라이언트에게 회신한다.

 => 처리가 완료되면 같은 오브젝트의 락 획득권을 대기중인 작업자에게 건넨다.

14. 모든 작업을 마친 작업자 A는 스레드 풀에 자신을 등록한다.

15 다음에 사용될 때까지 대기 상태에 들어간다.

정리 : 왜 SQL 서버는 독자의 스케줄 관리 기능을 구현할 필요가 있었을까?

 => SQL 서버가 CPU를 효과적으로 사용하기 위해서이다.

 

SQL 서버 내의 작업자 ( 스레드 )에는 원도우 OS가 갖고 있는 스케줄러가 이해하지 못하는 대기 상태가 수많이 존재한다.
때문에 모든 스케줄 관리를 윈도우 스케줄러에 맡겨 버리면 귀중한 CPU 리소스를 쓸데없이 낭비하게 되기 때문이다.

모든 것은 CPU를 보다 효율적으로 사용해서 큰 사이즈의 데이터베이스를 원활하게 관리하여 보다 많은 클라이언트의
요구에 신속하게 대처하기 위함이다. SQLOS스케줄러를 구현함으로써 지금까지는 주로 중소 규모 사에트에서 사용했던
SQL 서버는 엔터프라이즈 영역에서도 사용 가능한 스케이러빌리티를 갖게 되었다.

'' 카테고리의 다른 글

HTTP 완벽가이드  (0) 2022.08.09
HTTP 완벽 가이드  (0) 2022.08.06
HTTP 완벽 가이드  (0) 2022.08.01
HTTP 완벽 가이드  (0) 2022.07.25
그림으로 이해하는 SQL 서버의 구조  (0) 2022.07.17
Comments