SQL Server 是如何執行查詢指令碼的呢?首先,應用程式連線到SQL Server引擎, 向SQL Server傳送請求。一旦應用程式連線到資料庫引擎,SQL Server 建立會話(Session),用於表示客戶端和伺服器端之間資料交換的狀態。其次,SQL Server引擎分配Task來接受查詢請求,然後,SQL Server把Workder 繫結到Task,開始分配CPU資源和記憶體資源來處理Task。最後,通過解析、編譯和優化,進入查詢引擎,真正開始執行查詢請求。下圖從高層次上概括了SQL Server執行TSQL指令碼的流程:
對於圖中的相關元件,先來了解一下其基本的概念和功能,下面的術語都是在SQL Server OS中實現的。
一,Connections
連線,這是在底層協議上實現的實際的物理連線,在客戶端向資料庫引擎傳送請求之前,必須建立應用程式和資料庫引擎之間的物理連結,是應用程式和資料庫引擎之間聯絡的物理通道,有三種連線的型別:TCP socket,命名管道(named pipe)和共享記憶體(shared memory)。相關的DMV:sys.dm_exec_connections。
二,Sessions
會話,當客戶端應用程式連到SQL Server時,兩端就會建立起一個“會話”用於交換資訊。嚴格來說,會話不是底層的物理連線,是SQL Server對連線的邏輯表示,用於儲存在資料庫引擎和應用程式之間連線時所需要的設定,比如,登陸資訊,事務的隔離級別,會話的SET選項等。但是,在實際應用中,通常可以把會話視為連線。
當發現一個session有多個值時,意味著出現了並行查詢。一個並行查詢使用相同的會話來連線客戶端,但是在SQL Server端使用多個Worker(Thread)用於服務這個請求。相關DMV:sys.dm_exec_sessions,當你看到有多行資料有相同的Session ID時,這是因為SQL Server 使用多個執行緒來處理一個查詢請求。
通常情況下,一個Connection對應一個Session,有時,可能存在多個Session對應一個Connection的情況,這是MARS(Multiple Active Result Sets)現象。
三,Request
請求,在SQL Server 的語境下,是查詢或批處理的邏輯表示。SQL Server是一個 Client-Server平臺,客戶端與伺服器(後端資料庫)互動的唯一方式是通過傳送包含命令的請求到資料庫,而客戶端與伺服器端互動的協議簡稱為TDS(Tabular Data Stream)。應用程式使用SqlClient、OleDB、ODBC、JDBC等驅動來實現這種協議。當應用程式需要對資料庫執行任何操作時,它都通過TDS協議向資料庫引擎傳送一個請求(request)。
簡單來說,每次對資料庫的操作都會以“請求”的形式傳送給資料庫伺服器,傳送請求有多種型別,主要是:Batch Request、RPC Request、Bulk Load Request。
- Batch Request:批請求,此請求型別僅包含要執行的批處理的T-SQL文字。
- RPC Request:遠端過程呼叫請求(Remote Procedure Call Request),用於執行儲存過程。
- Bulk Load Request:大容量載入請求,用於執行大容量插入(Bulk Insert)操作
相關的DMV是:sys.dm_exec_requests。
四,Tasks
任務,表示SQL Server 需要處理的一個請求。一個或多個Task會被分配用於完成一個請求。在一個完整的TDS請求達到資料庫引擎時,SQL Server將會建立一個任務(Task)來處理請求。當Request到達SQL Server之後,後續操作都發生在SQL Server內部。使用者可以從 sys.dm_exec_requests 檢視資料庫引擎接收到的所有請求。一旦一個Task接收一個請求,Task的狀態由PENDING 轉變為可用。
當任務被建立用於處理請求時,該Task將代表請求從開始到完成的整過程。例如,如果請求是SQL Batch型別的請求,則任務將代表整個批次,而不是單個語句,SQL Batch中的單個語句不會建立新任務。批處理中的某些個別語句可以並行執行(通常稱為DOP,並行度),在這種情況下,任務將產生新的子任務以並行執行。如果請求返回結果,則當客戶端完全使用結果時(例如,當您處置SqlDataReader時),批處理就完成了。您可以通過查詢sys.dm_os_tasks 來檢視伺服器中的任務列表。
當一個新請求到達伺服器並且建立一個對應的任務時,首先會處於PENDING(掛起狀態),任務的狀態可以有:
- PENDING:正在等待工作執行緒(Worker Thread)。
- RUNNABLE:可執行,但正在等待接收一個時間片(quantum)。
- RUNNING:當前正在Scheduler中執行。
- SUSPENDED:擁有worker,但是正在等待某些事件(向RUNNABLE轉變)
- DONE:已經完成。
- SPINLOOP:陷入自旋鎖。
當新的請求到達伺服器並建立任務以處理該請求時,處於PENDING狀態。在此階段,伺服器尚不知道請求實際上是什麼。該任務必須首先開始執行,為此,引擎必須為其分配一個工作程式(Worker)。
五,Workers
工作程式(Workder Thread),簡稱為Workder,或Thread,邏輯上對應於作業系統的執行緒,執行緒是作業系統可以執行的最小處理單元,並允許將應用程式邏輯上分為多個併發執行路徑。SQL Server伺服器在啟動時會建立一定數量的Worker(工作程式),並且可以按需建立更多工作程式,直到配置的最大工作程式執行緒(max worker threads)。只有Worker才能執行程式碼,Worker等待PENDING任務變為可用(當Task被分配用於處理請求),然後每個Workder被分配到一個Task,並執行該Task。Workder會一直執行(running),直到任務完全完成。
當沒有更多可用的Worker(工作程式)時,正在等待處理(PENDING)的任務將不得不等待,直到正在執行的(running)任務完成,或者執行該任務的Workder變得可用,能夠執行下一個PENDING的任務為止。
對於一個SQL批處理請求,承擔該任務的工作程式將執行整個SQL批處理(每個語句)。對於SQL批處理中的語句(=> request => task => worker)是否可以並行執行,答案是否定的,因為它們是在單個執行緒(=> worker)上執行的,所以每個語句必須按照順序來執行。
對於使用並行選項(DOP> 1)的語句,SQL Server會建立子任務,每個子任務都會經歷完全相同的週期:建立子任務(PENDING),工作程式必須拾取子任務並執行(與SQL批處理工作者不同的工作程式),通過查詢sys.dm_os_workers可以檢視SQL Server中工作程式的列表和狀態。
六,Scheduler
排程程式(Scheduler)是指SOS scheduler,用於管理Worker對CPU時間的需求,協調各個Worker對CPU資源的利用。每一個Scheduler都對映到一個單獨的CPU,Workder在一個排程程式中保持活躍/執行(Running)的時間稱作一個時間片(Quantum),最長時長為4毫秒。在其時間片到期之後,一個Worker主動退出,把時間片讓給其他需要訪問CPU資源的Workder,並修改自身的狀態為RUNNABLE,這種排程方式稱為非搶佔式排程。
SOS Scheduler是非搶佔式的,資料庫對各種請求分配的時間都是相同的,而作業系統的排程模式搶佔式的,當出現緊急情況時,按照優先順序,高優先順序程式搶佔把低優先順序低的資源。
參考文件:
Thread and Task Architecture Guide
Understanding how SQL Server executes a query