並行執行作為提升查詢響應時間,提高用戶體驗的一種有效手段被大家所熟知,感興趣的朋友可以看我以前的博客SQL Server優化技巧之SQL Server中的"MapReduce", SQL Server優化器特性-位圖過濾(Bitmap),然而正如我一直強調的,任何事物均有利弊,重點在於抉擇.近日有朋友問我關於在今年7月份SQL Saturday中分享的並行執行中關於並行死鎖的內容,這里我就詳細解釋下我舉的實例中的並行死鎖.
並行死鎖我們可以理解為SQL Server的”BUG”,即按照並行方式執行時遭遇特定異常使得執行無法繼續.官方給的解決方式大多很簡單,改為串行就可以了.但其中還是有值得我們深究的地方以便我們更好的利用並行.
注:由於案例涉及的並行知識點較多,缺乏相關知識的同學可以先預覽一下我當時分享的PPT,如果有不清楚的地方也可以私信我. Inside Parallel Query
閑篇到此,上實例
生成測試數據

CREATE TABLE Numbers ( Number INT NOT NULL, CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) WITH FILLFACTOR = 100 ) INSERT INTO Numbers SELECT (a.Number * 256) + b.Number AS Number FROM ( SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255 ) a (Number), ( SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255 ) b (Number)
接下來我們執行如下語句,取30000下最大偶數,此時我將執行並行數maxdop隨意調整為奇數,3,5,7我的執行都可以迅速返回結果.
執行代碼

set statistics time on select maxN=max(num.number) from dbo.numbers as num where num.number<30000 and convert(integer,convert(varchar(max),num.number)) % 2=0 option ( Maxdop 3,-----5,7 querytraceon 8649 );
但當我將並行數調整為偶數時,執行時間居然長達數秒…打開trace profiler跟蹤dead lock chain我們發現,當並行數為偶數時出現了死鎖.
如圖1-1,1-2,1-3

select maxN=max(num.number) from dbo.numbers as num where num.number<30000 and convert(integer,convert(varchar(max),num.number)) % 2=0 option ( Maxdop 4,-----2,6 querytraceon 8649 );
圖1-1
圖1-2
圖1-3
咋一看有的同學可能覺得蹊蹺,Bug可能是很多人的第一反應.而到底發生了什么由我們具體分析下並行死鎖的相應執行計划
分析:
1訪問基表數據時用的是聚集索引掃描,但掃描方式是backward,而SQL server中只有forward scan可以並行掃描,backward只能串行掃描
2 因此在做exchange向各個threads分發數據時(distribute streams)采用roundrobin輪詢分發數據,這勢必造成奇偶數據按threads分開流向下一個過濾操作符
3 在Filter時將奇數的數據過濾,而相應的threads也就沒有了數據
4 所以在最后exchange匯總數據時(gather streams)有的threads沒有數據,因而造成死鎖.
注:thread 0為主線程,不參與並行分支工作
分析如圖2-1
圖2-1
而反觀並行采用奇數並行數,這時當分發數據時就不會造成某個thread所持有的數據只是奇數或是偶數,也就不會造成后來的情形,死鎖也就不會出現.如圖2-2感興趣的同學可以做實驗調整並行數並閱讀相應的執行計划.
圖2-2
小結:要讀懂並行執行計划需要一定的知識儲備,不了解的同學可先看我分享的PPT.並行執行做為提升復雜查詢響應時間的殺手鐧被很多數據庫廠商廣泛應用,而其中的部分思想也應用的分布式系統當中,可以說當下硬件架構下並行是執行引擎健壯性的重要體現.
結語:萬事皆有因果,一個簡單的BUG可以做為回應,但深究可能窺其本質,並且很有意思.技術人需有這種精神.
認為有收獲的同學請點贊.