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