SqlServer應(yīng)用之sys.dm_os_waiting_tasks 引發(fā)的疑問(中)
通過上篇文章給大家介紹了SqlServer應(yīng)用之sys.dm_os_waiting_tasks 引發(fā)的疑問(上) ,說了一下sys.dm_exec_requests 和 sys.dm_os_waiting_tasks 在獲取并行等待的時(shí)候得不同結(jié)果,這一篇我們談?wù)撓挛业牡诙€(gè)疑問:為什么一個(gè)并行計(jì)劃(4線程)卻一下出現(xiàn)了那么多等待,SQL的并行到底是怎么執(zhí)行的?。。。?br />
先貼以下上篇sys.dm_os_waiting_tasks 的結(jié)果圖:

我們分析一下這個(gè)結(jié)果的task_address 可以看出去掉重復(fù)其實(shí)只有9個(gè),也就是說一個(gè)并行(4線程,配置不同,情況也不同)會有9個(gè)task。 又是線程,又是task ,還有worker,schedulers 這些都是什么? 這個(gè)有必要先說一下,因?yàn)檫@篇博客前我也是亂亂的。
scheduler
對于每個(gè)邏輯CPU,SQLSERVER會有一個(gè)scheduler與之對應(yīng),在SQL層面上代表CPU對象,只有拿到scheduler所有權(quán)的任務(wù)worker才能在這個(gè)邏輯CPU上運(yùn)行
所謂邏輯CPU,就是SQLSERVER從Windows層面上看到的CPU數(shù)目,如果是一個(gè)雙核的CPU,那么一個(gè)物理CPU在SQL看來就是兩個(gè)邏輯CPU。如果系統(tǒng)還使用了
超線程hyper-threaded ,那對SQLSERVER來講就是4個(gè)邏輯CPU
規(guī)則: 每個(gè)scheduler上的最大worker數(shù)目等于SQLSERVER的最大線程數(shù)除以scheduler的數(shù)目 ,在同一個(gè)時(shí)間點(diǎn),只能有一個(gè)擁有scheduler的worker處于運(yùn)行狀態(tài),其他worker都必須處于等待狀態(tài)。這樣能降低每個(gè)邏輯CPU上的處于正在運(yùn)行狀態(tài)的線程數(shù)目,降低context switch,提供可擴(kuò)展性scheduler是SQLSERVER的一個(gè)邏輯概念,他不與物理CPU相綁定。也就是說,一個(gè)scheduler可以被Windows安排一會兒在這個(gè)CPU上,一會兒在那個(gè)CPU上。
但是,如果在sp_configure里設(shè)置了CPU affinity mask,那么scheduler就會固定在某個(gè)特定的CPU上
worker
每個(gè)worker跟一個(gè)線程(或纖程fiber)相對應(yīng),是SQLSERVER任務(wù)的執(zhí)行單位。SQLSERVER不直接調(diào)度線程/纖程,而是調(diào)度worker,使得SQLSERVER能夠控制
任務(wù)調(diào)度
規(guī)則: 每個(gè)worker會固定代表一個(gè)線程(或纖程),并且和一個(gè)scheduler相綁定。如果scheduler是固定在某個(gè)CPU上的(通過設(shè)置CPU affinity mask),那么worker也會固定在某個(gè)CPU上每個(gè)scheduler有worker的上限值,并且可以根據(jù)SQLSERVER工作負(fù)荷創(chuàng)建或釋放worker,每次worker都會去運(yùn)行一個(gè)完整的任務(wù)(task)。在任務(wù)做完之前不會退出,除非這個(gè)任務(wù)主動進(jìn)入等待狀態(tài)。
scheduler只在有新任務(wù)要運(yùn)行,而當(dāng)前沒有空閑的worker的情況下,才會創(chuàng)建新的worker。
某個(gè)worker空閑超過15分鐘,scheduler可能會刪除這個(gè)worker,以及其對應(yīng)的線程。當(dāng)SQLSERVER遇到內(nèi)存壓力的時(shí),也會大量刪除處于空閑狀態(tài)的worker,以節(jié)省multi-page的內(nèi)存開銷各種CPU和SQLSERVER版本組合自動配置的最大工作線程數(shù)CPU數(shù) 32位計(jì)算機(jī) 64位計(jì)算機(jī)
<=4 256 512
8 288 576
16 352 704
32 480 960
task
在worker上運(yùn)行的最小任務(wù)單元。最簡單的task就是一個(gè)簡單batch。例如,客戶發(fā)過來下面的請求:
SELECT @@SERVERNAME
GO
SELECT GETDATE()
GO
那么這兩個(gè)batch就分別是兩個(gè)task。SQLSERVER會先分配給第一個(gè)batch(select @@servername)一個(gè)worker,將結(jié)果返回給客戶端,再分配第二個(gè)batch
(select getdate())一個(gè)worker。這兩個(gè)worker可能是不同的worker,甚至在不同的scheduler上只要一個(gè)task開始運(yùn)行,他就不會從這個(gè)worker上被移出。例如,如果一個(gè)select語句被其他連接阻塞住,worker就不能繼續(xù)運(yùn)行,只能進(jìn)入等待狀態(tài)。但是這個(gè)select task 不會將這個(gè)worker釋放,讓他做其他任務(wù)。所以結(jié)果是這個(gè)worker所對應(yīng)的線程會進(jìn)入等待狀態(tài)
yielding
SQLOS的任務(wù)調(diào)度算法的核心,就是所有在邏輯scheduler上運(yùn)行的worker都是非搶占式的 (non-preemptive)。worker始終在scheduler上運(yùn)行,直到他運(yùn)行結(jié)束,或者主動將scheduler讓出給其他worker為止。這個(gè)“讓出”scheduler的動作,我們叫yieding每個(gè)scheduler都會有一個(gè)runnable列表,所有等待CPU運(yùn)行的worker都會在這個(gè)列表里排隊(duì),以先進(jìn)先出的算法,等待SQL分配給他scheduler運(yùn)行SQLSERVER定義了很多yieding的規(guī)則,約束一個(gè)task在scheduler運(yùn)行的時(shí)間。如果task比較復(fù)雜,不能很快完成,會保證task在合適的時(shí)間點(diǎn)做yieding,不至于占用scheduler太多時(shí)間。
常見時(shí)間點(diǎn):
1、當(dāng)worker每次要去讀數(shù)據(jù)頁的時(shí)候,SQLSERVER會檢查這個(gè)worker已經(jīng)在scheduler上運(yùn)行了多久,如果已經(jīng)超過4ms,就做yielding
2、每做64KB的結(jié)果集排序,就會做一次yielding
3、在做語句編譯compile的過程中(這個(gè)過程比較占CPU資源),經(jīng)常會有yieding
4、如果客戶端不能及時(shí)把結(jié)果集取走,worker就會做yieding
5、一個(gè)batch里的每一句話做完,都會做一次yieding
正常來講,哪怕一個(gè)task要做很久,他使用的worker是會經(jīng)常做yieding的,不會長時(shí)間占用CPU不放。如果在一個(gè)scheduler上同時(shí)有很多worker要運(yùn)行,SQLSERVER通過worker自動yielding的方式調(diào)度并發(fā)運(yùn)行。這個(gè)比Windows用上下文切換context switch更有效
另附一張手繪圖

另外推薦一篇 SQL SERVER SQLOS的任務(wù)調(diào)度 微軟亞太的官方博客
我們大概了解了一下SQL SERVER SQLOS的任務(wù)調(diào)度 我們回到我們的并行話題看一下這個(gè)并行執(zhí)行的調(diào)度情況:

一個(gè)并行處理分配給了9個(gè)task,同時(shí)也啟用了9個(gè)worker,由4個(gè)scheduler調(diào)度,每個(gè)scheduler分別由一個(gè)申請數(shù)據(jù),另一個(gè)等待。那么申請數(shù)據(jù)的是可以理解的,等待的是干什么的呢?個(gè)人理解和當(dāng)前的執(zhí)行計(jì)劃有關(guān),4個(gè)線程取得數(shù)據(jù)后要做匯總的操作SQL不會等待數(shù)據(jù)獲取以后再開啟線程接收,而是接收線程在獲取數(shù)據(jù)的時(shí)候等待。


疑問得到解決了么?因?yàn)橐呀?jīng)標(biāo)記為中篇,可見還是有疑問呀?。。?!我們繼續(xù)下一篇吧....
相關(guān)文章
在SQL SERVER中導(dǎo)致索引查找變成索引掃描的問題分析
SQL Server 中什么情況會導(dǎo)致其執(zhí)行計(jì)劃從索引查找(Index Seek)變成索引掃描(Index Scan)呢? 下面從幾個(gè)方面結(jié)合上下文具體場景做了下測試、總結(jié)、歸納。需要的朋友可以參考下本文2015-09-09
insert select與select into 的用法使用說明
將一個(gè)table1的數(shù)據(jù)的部分字段復(fù)制到table2中,或者將整個(gè)table1復(fù)制到table2中,這時(shí)候我們就要使用SELECT INTO 和 INSERT INTO SELECT 表復(fù)制語句了2011-12-12
SQL Server中T-SQL 數(shù)據(jù)類型轉(zhuǎn)換詳解
T-SQL提供了兩個(gè)顯示轉(zhuǎn)換的函數(shù):CAST函數(shù)和CONVERT函數(shù)。今天我們就來相信探討下2018-02-02
sqlserver 2000數(shù)據(jù)庫同步 同步兩個(gè)SQLServer數(shù)據(jù)庫的內(nèi)容
程序代碼可以有版本管理CVS進(jìn)行同步管理,可是數(shù)據(jù)庫同步就非常麻煩,只能自己改了一個(gè)后再去改另一個(gè),如果忘記了更改另一個(gè)經(jīng)常造成兩個(gè)數(shù)據(jù)庫的結(jié)構(gòu)或內(nèi)容上不一致.2010-05-05

