前言
上三篇文章我們介紹了查看查詢計劃的方式,以及一些常用的連接運算符、聯合運算符的優化技巧。
本篇我們分析SQL Server的并行運算,作為多核計算機盛行的今天,SQL Server也會適時調整自己的查詢計劃,來適應硬件資源的擴展,充分利用硬件資源,最大限度的提高性能。
閑言少敘,直接進入本篇的正題。
技術準備
同前幾篇一樣,基于SQL Server2008R2版本,利用微軟的一個更簡潔的案例庫(Northwind)進行解析。
一、并行運算符
在我們日常所寫的T-SQL語句,并不是所有的最優執行計劃都是一樣的,其最優的執行計劃的形成需要多方面的評估才可以,大部分根據SQL Server本身所形成的統計信息,然后對形成的多個執行計劃進行評估,進而選出最優的執行方式。
在SQL Server根據庫內容形成的統計信息進行評估的同時,還要參照當前運行的硬件資源,有時候它認為最優的方案可能當前硬件資源不支持,比如:內存限制、CPU限制、IO瓶頸等,所以執行計劃的優劣還要依賴于底層硬件。
當SQL Server發現某個處理的數據集比較大,耗費資源比較多時,但此時硬件存在多顆CPU時,SQL Server會嘗試使用并行的方法,把數據集拆分成若干個,若干個線程同時處理,來提高整體效率。
在SQL Server中可以通過如下方法,設置SQL Server可用的CPU個數
默認SQL Server會自動選擇CPU個數,當然不排除某些情況下,比如高并發的生產環境中,防止SQL Server獨占所有CPU,所以提供了該配置的界面。
還有一個系統參數,就是我們熟知的MAXDOP參數,也可以更改此系統參數配置,該配置也可以控制每個運算符的并行數(記住:這里是每個運算符的,而非全部的),我們來查看該參數
這個并行運算符的設置數,指定的是每個運算符的最大并行數,所以有時候我們利用查看系統任務數的DMV視圖sys.dm_os_tasks來查看,很可能看到大于并行度的線程數據量,也就是說線程數據可能超過并行度,原因就是兩個運算符重新劃分了數據,分配到不同的線程中。
這里如沒特殊情況的話,建議采用默認設置最佳。
我們舉一個分組的例子,來理解并行運算
采用并行運算出了提升性能還有如下幾個優點:
- 不依賴于線程的數量,在運行時自動的添加或移除線程,在保證系統正常吞吐率的前提下達到一個性能最優值
- 能夠適應傾斜和負載均衡,比如一個線程運行速度比其它線程慢,這個線程要掃描或者運行的數量會自動減少,而其它跑的快的線程會相應提高任務數,所以總的執行時間就會平穩的減少,而非一個線程阻塞整體性能。
下面我們來舉個例子,詳細的說明一下
并行計劃一般應用于數據量比較大的表,小表采用串行的效率是最高的,所以這里我們新建一個測試的大表,然后插入部分測試數據,我們插入250000行,整體表超過6500頁,腳本如下
--新建表,建立主鍵,形成聚集索引 CREATE TABLE BigTable ( [KEY] INT, DATA INT, PAD CHAR(200), CONSTRAINT [PK1] PRIMARY KEY ([KEY]) ) GO --批量插入測試數據250000行 SET NOCOUNT ON DECLARE @i INT BEGIN TRAN SET @i=0 WHILE @i<250000 BEGIN INSERT BigTable VALUES(@i,@i,NULL) SET @i=@i+1 IF @i%1000=0 BEGIN COMMIT TRAN BEGIN TRAN END END COMMIT TRAN GO
我們來執行一個簡單查詢的腳本
SELECT [KEY],[DATA]
FROM BigTable
這里對于這種查詢腳本,沒有任何篩選條件的情況下,沒必要采用并行掃描,因為采用串行掃描的方式得到數據的速度反而比并行掃描獲取的快,所以這里采用了clustered scan的方式,我們來加一個篩選條件看看
SELECT [KEY],[DATA] FROM BigTable WHERE DATA<1000
對于這個有篩選條件的T-SQL語句,這里SQL Server果斷的采用的并行運算的方式,聚集索引也是并行掃描,因為我電腦為4個邏輯CPU(其實是2顆物理CPU,4線程),所以這里使用的是4線程并行掃描四次表,每個線程掃描一部分數據,然后匯總。
這里總共用了4個線程,其中線程0為調度線程,負責調度所有的其它線程,所以它不執行掃描,而線程1到線程4執行了這1000行的掃描!當然這里數據量比較少,有的線程分配了0個任務,但是總得掃描次數為4次,所以這4個線程是并行的掃描了這個表。
可能上面獲取的結果比較簡單,有的線程任務還沒有給分配滿,我們來找一個相對稍復雜的語句
SELECT MIN([DATA])
FROM BigTable
這個執行計劃挺簡單的,我們依次從右邊向左分析,依次執行為:
4個并行聚集索引掃描——>4個線程并行獲取出前當前線程的最小數——>執行4個最小數匯總——>執行流聚合獲取出4個數中的最小值——>輸出結果項。
然后4個線程,每個線程一個流聚合獲取當前線程的最小數
然后,將這個四個最小值經過下一個“并行度”的運算符匯聚成一個表
然后下一個就是流聚合,從這個4行數據中獲取出最小值,進行輸出,關于流聚合我們上一篇文章中已經介紹
以上就一個一個標準的多線程并行運算的過程。
上面的過程中,因為我們使用的并行聚集索引掃描數據,4個線程基本上是平均分攤了任務量,也就是說每個線程掃描的數據量基本相等,下面我們將一個線程使其處于忙碌狀態,看看SQL Server會不會將任務動態的平攤到其它幾個不忙碌的線程上。
我們在來添加一個大數據量表,腳本如下
SELECT [KEY],[DATA],[PAD]
INTO BigTable2
FROM BigTable
我們來寫一個大量語句的查詢,使其占用一個線程,并且我們這里強制指定只用一個線程運行
SELECT MIN(B1.[KEY]+B2.[KEY]) FROM BigTable B1 CROSS JOIN BigTable2 B2 OPTION(MAXDOP 1)
以上代碼想跑出結果,就我這個電腦配置估計少說五分鐘以上,并且我們還強行串行運算,速度可想而知,
我們接著執行上面的獲取最小值的語句,查看執行計劃
SELECT MIN([DATA])
FROM BigTable
我們在執行計劃中,查看到了聚集索引掃描的線程數量
可以看到,線程1已經數量減少了近四分之的數據,并且從線程1到線程4,所掃描的數據量是依次增加的。
我們上面的語句很明確的指定了MAXDOP為1,理論上講只可能會影響一個線程,為什么這幾個線程都影響呢?其實這個原因很簡單,我的電腦是物理CPU只有兩核,所謂的線程數只是超線程,所以非傳統意義上的真正的4核數,所以線程之間是互相影響的。
我們來看一個并行連接操作的例子,我們查看并行嵌套循環是怎樣利用資源的
SELECT B1.[KEY],B1.DATA,B2.DATA FROM BigTable B1 JOIN BigTable2 B2 ON B1.[KEY]=B2.[KEY] WHERE B1.DATA<100
上面的語句中,我們在BigTable中Key列存在聚集索引,而查詢條件中DATA列不存在,所以這里肯定為聚集索引掃描,對數據進行查找
來看執行計劃
我們依次來分析這個流程,結合文本的執行計劃分析更為準確,從右邊依次向左分析
第一步,就是利用全表通過聚集索引掃描獲取出數據,因為這里采用的并行的聚集索引掃描,我們來看并行的線程數和掃描數
四個線程掃描,這里線程3獲取出數據100行數據。
然后將這100行數據,重新分配線程,這里每個線程平均分配到25行數據
到此,我們要獲取的結果已經均分成4個線程共同執行,每個線程分配了25行數據,下一步就是交給嵌套循環連接了,因為我們上面的語句中需要從BigTable2中獲取數據行,所以這里選擇了嵌套循環,依次掃描BigTable2獲取數據。
關于嵌套循環連接運算符,可以參照我的第二篇文章。
我們知道這是外表的循環數,也就是說這里會有4個線程并行執行嵌套循環。如果每個線程均分25行,數據那么內部表就要執行
4*25=100次。
然后,執行完,嵌套掃描獲取結果后,下一步就是,將各個線程執行的結果通過并行運算符匯總,然后輸出
上述過程就是一個并行嵌套循環的執行流程。充分利用了四核的硬件資源。
參考文獻
- 微軟聯機叢書邏輯運算符和物理運算符引用
- 參照書籍《SQL.Server.2005.技術內幕》系列
結語
此篇文章先到此吧,文章短一點,便于理解掌握,后續關于并行操作還有一部分內容,后續文章補充吧,本篇主要介紹了查詢計劃中的并行運算符,下一篇我們接著補充一部分SQL Server中的并行運算,然后分析下我們日常所寫的增刪改這些操作符的優化項,有興趣可提前關注,關于SQL Server性能調優的內容涉及面很廣,后續文章中依次展開分析。
有問題可以留言或者私信,隨時恭候有興趣的童鞋加入SQL SERVER的深入研究。共同學習,一起進步。
文章最后給出上一篇的連接
如果您看了本篇博客,覺得對您有所收獲,請不要吝嗇您的“推薦”。
文章列表