SQL Server-聚焦查詢計劃Stream Aggregate VS Hash Match Aggregate(二十)

前言

以前系列中在查詢計劃中一直出現Stream Aggregate,當時也只是作了基本瞭解,對於查詢計劃中出現的操做,咱們都須要去詳細研究下,只有這樣才能對查詢計劃執行的每一步操做都瞭如指掌,因此纔有了本文的出現,簡短的內容,深刻的理解,Always to review the basics。函數

Stream Aggregate

Stream Aggregate經過單列或者多列來對行進行分組而且對指定的查詢來計算聚合表達式。最多見的聚合類型如SUM、COUNT、SUM、AVG、MIN、MAX,當咱們執行這些聚合函數時在查詢計劃中就會出現Stream Aggregate,Stream Aggregate是很是快的,由於它須要在輸入時經過在GROUP BY中指定的列進行排序。若是聚合中的數據沒有進行排序此時會經過Sort進行預排序或者使用索引查找或者索引掃描來提早預排序數據。以前咱們討論過出現Stream Aggregate有三種方式分別爲:聚合函數聚合,分組聚合,DISTINCT聚合,實際上只有兩種,DISTINCT內部就用到了分組,這裏咱們將Stream Aggregate分爲兩種類型,一種是標量聚合,另一種則是分組聚合。咱們舉一個標量聚合的例子,也就是返回單值聚合。性能

標值聚合

USE TSQL2012
GO

SELECT COUNT(*)
FROM Sales.Orders

下面咱們再來分組聚合的例子優化

USE TSQL2012
GO

SELECT custid
FROM Sales.Orders
GROUP BY custid

上述就是Stream Aggregate兩種類型的例子,關於標量聚合比較簡單直接利用聚合函數就行,下面咱們主要詳細講解這兩種類型中的分組聚合。spa

分組聚合

咱們來結合SQL Server 2012基礎教程來看一個簡單的例子code

USE TSQL2012
GO

SELECT custid, COUNT(shipcity) AS [shipcity_count]
FROM Sales.Orders
GROUP BY custid

上述查詢計劃比較簡單咱們來解釋下,首先經過默認主鍵建立的彙集索引來讀取表中行數據,接着經過GROUP BY上指定的列custid來進行排序,咱們看到其排序操做具體信息就知道,以下。接着遍歷全部custid,全部行被讀取,開始一行行讀取並計算其聚合表達式的值。重複處理直到完成爲止。blog

對於經過流聚合對custid進行分組的示意圖大概以下:排序

上述因爲未對custid建立索引致使因此會經過Sort來進行排序,毫無疑問致使查詢緩慢,這裏咱們對custid建立非彙集索引再來看看狀況教程

CREATE NONCLUSTERED INDEX idx_nc_custid ON Sales.Orders(custid)

 

此時查詢將會充分利用索引,它會經過使用索引排序來進行聚合計算,因此就不會再利用Sort來排序致使性能低下,經過上述咱們知道,在進行Stream Aggregate以前事實上在指定的分組列上建立索引來預先排序會提升查詢性能,而不須要再去利用Sort進行排序而耗費沒必要要的時間。上述咱們已經說過在進行排序要麼在GROUP BY上指定的列經過建立索引查找或者索引排序,若是GROUP BY中的列沒有建立索引此時利用Sort來進行顯示排序,以下顯示指定ORDER BY custid來排序和沒有指定的話結果依然都是使用Sort來排序,此時Stream Aggregate,其實這種說法不太準確,由於在SQL Server中有兩種聚合方式,一種是Stream Aggregate,另一種則是Hash Match Aggregate。索引

USE TSQL2012
GO

SELECT custid, COUNT(shipcity) AS [shipcity_count]
FROM Sales.Orders
GROUP BY custid
ORDER BY custid

自從SQL Server 7以後就出現了Stream Aggregate和Hash Aggregate兩種聚合方式,也就是說上述咱們稍做修改查詢計劃就變成了Hash Aggregate的形式。ip

USE TSQL2012
GO

DBCC RULEOFF('GbAggToStrm');
GO

SELECT custid, COUNT(shipcity) AS [shipcity_count]
FROM Sales.Orders
GROUP BY custid
OPTION(RECOMPILE)
GO
 DBCC RULEON('GbAggToStrm');

上述GbAggToStrm是什麼鬼,其實若是查詢計劃中走的Stream Aggregate操做的話,也就說它走的是GbAggToStrm規則(GROUP BY Aggregate To Stream ),可是這裏咱們關閉了查詢計劃本該走的Stream Aggregate操做即GbAggToStrm規則,因此此時它將只能走Hash Aggregate。因此到這裏說明在排序時即便指定了ORDER BY操做有多是多餘的,可是若是咱們不指定的話,要是咱們但願返回的結果集是排序的,此時要是走的Hash Aggregate,結果返回的結果集將是無序的,致使咱們得不到想要的結果集,因此仍是但願在排序時指定ORDER BY操做,這樣可以避免沒必要要的狀況發生。

DISTINCT在Hash Match Aggregate和Stream Aggregate和DISTINCT Sort中的使用

當查詢中用到了DISTINCT關鍵字時,此時查詢計劃有可能走Stream Aggregate,也有可能走的是Hash Match Aggregate。因此在這裏咱們分析下何時會用Hash Match Aggregate,何時又會用到Stream Aggregate。說到底DISTINCT關鍵字時用來去重的,在SQL Server中利用DISTINCT關鍵字來去重其查詢計劃走的方式分爲兩種,一種是在哈希表中創建惟一值,另一種則是將行進行排序分配到組中而後只返回組中的一個值便可。因此在SQL Server中使用Hash Match Aggregate來實現哈希表,使用Stream Aggregate或者DISTINCT Sort來對數據進行排序去重。

使用DISTINCT關鍵字走DISTINCT Sort

當咱們以下直接利用DISTINCT來查詢時就是利用的DISTINCT Sort來排序去重。

USE TSQL2012
GO

select DISTINCT custid 
FROM Sales.Orders

雖然很明確走的Sort,可是這是通過SQL查詢引擎優化事後纔有的,最原始的狀況是先進行Sort接着進行Stream Aggregate,下面咱們關閉Sort的規則看看。

USE TSQL2012
GO

DBCC RULEOFF('GbAggToSort')
SELECT DISTINCT custid
FROM Sales.Orders
OPTION(RECOMPILE)
DBCC RULEON(
'GbAggToSort')

使用DISTINCT關鍵字走Hash Match Aggregate

當未在列SomeColumn建立索引時咱們進行以下查詢

USE TSQL2012
GO

SELECT DISTINCT SomeColumn
FROM dbo.BigTable

接下來咱們在列上建立索引

CREATE NONCLUSTERED INDEX idx_noncls_somecolumn ON dbo.BigTable(SomeColumn)

在建立索引時此時查詢計劃走的倒是Stream Aggregate,也就是說當利用DISTINCT關鍵字查詢時且列已經進行了排序,此時查詢計劃走Stream Aggregate。那何時用Hash Match Aggregate呢,上述對列未建立索引時走的是Hash Match Aggregate由於數據量比較大此時還利用了並行計算,換句話說當對列未建立索引時且數據量很是大同時分組比較少時,查詢計劃更加更傾向於走Hash Match Aggregate,輸入大量的數據經過Hash Match Aggregate結合並行計算效率也很是高,固然分組較少更好,此時不會太佔用哈希表。接下來咱們限制查詢結果集的條數。

USE TSQL2012
GO

SELECT DISTINCT TOP 10 SomeColumn
FROM dbo.BigTable

此時查詢計劃再也不是Hash Match Aggregate代替的是Hash Match(Flow Distinct)咱們看下msdn關於Flow Distinct的解釋:Flow Distinct邏輯運算符用於經過掃描輸入來刪除重複項。雖然Distinct 運算符在生成任何輸入前消耗全部的輸入,但FlowDistinct 運算符在從輸入得到行時返回每行(除非該行是一個重複項,如果這樣則刪除該行)

也就是說DISTINCT直接就過濾了重複行,而Flow Distict則得到每行時並返回每一行,這就是Flow Distinct,它的出現依賴於在查詢計劃中估計惟一值的數量,當咱們將TOP的數量設置爲接近100萬或者比100萬還少一點時此時走的是Hash Match Aggregate。到此咱們關於Hash Match Aggregate和Stream Aggregate的分析算是結束了,咱們下個基本結論:

Hash Match Aggregate和Stream Aggregate分析結論:

(1)查詢中有DISTINCT關鍵字時:當在查詢列上建立索引時即列進行了排序時此時走Stream Aggregate,當數據量很是大時且未建立索引時此時通常走的是Hash Match Aggregate並結合並行計算,其他狀況則是走的Distinct Sort。

(2)查詢中沒有DISTINCT關鍵字時,對於標量聚合和分組聚合走的是Stream Aggregate。

總結

好了本節關於Hash Match Aggregate和Stream Aggregate的介紹就到此爲止,基本算是瞭解,太複雜的也沒去過多探討,這是DBA的事情了,下一節咱們穿插講講關於計算列持久化系列文章,簡短的內容,深刻的理解,咱們下節再會。 

相關文章
相關標籤/搜索