Sql-Server

雜湊聚合救助

  • July 17, 2019

聊天討論中出現的一個問題:

我知道雜湊連接救助會在內部切換到一種嵌套循環的東西。

SQL Server 對散列聚合救助做了什麼(如果它可能發生的話)?

散列連接散列聚合都在內部使用相同的運算符程式碼,儘管散列聚合僅使用單個(建構)輸入。Craig Freedman 描述了雜湊聚合的基本操作:

與散列連接一樣,散列聚合需要記憶體。在使用散列聚合執行查詢之前,SQL Server 使用基數估計來估計我們需要多少記憶體來執行查詢。使用散列連接,我們儲存每個建構行,因此總記憶體需求與建構行的數量和大小成正比。連接的行數和連接的輸出基數對連接的記憶體要求沒有影響。使用散列聚合,我們為每個組儲存一行,因此總記憶體需求實際上與輸出組或行的數量和大小成正比。如果我們按列分組的唯一值和組更少,我們需要更少的記憶體。如果我們有更多的按列分組的唯一值和更多的組,我們需要更多的記憶體。

他繼續談論雜湊遞歸:

那麼,如果我們的記憶體不足會發生什麼?同樣,像雜湊連接一樣,如果記憶體不足,我們必須開始將行溢出到 tempdb。我們溢出一個或多個儲存桶或分區,包括任何部分聚合的結果以及散列到溢出的儲存桶或分區的任何其他新行。雖然我們不嘗試聚合溢出的新行,但我們會對它們進行雜湊處理並將它們分成幾個桶或分區。一旦我們完成了所有輸入組的處理,我們就會輸出完成的記憶體組,並通過一次回讀和聚合一個溢出的分區來重複算法。通過將溢出的行分成多個分區,我們減小了每個分區的大小,從而降低了算法需要重複多次的風險。

救助

Hash bailout的文件很少,但 Nacho Alonso Portillo 在What’s the maximum level of recursion for the hash iterator before force bail-out 中提到過?

該值是產品中硬編碼的常數,其值為五 (5)。這意味著,在散列掃描運算符對任何不適合工作區授予的記憶體的給定子分區使用基於排序的算法之前,之前必鬚髮生五次將原始分區細分為較小分區的嘗試。

提到的“雜湊掃描運算符”引用CQScanHashsqlmin.dll. 這個類負責實現我們在執行計劃中看到的散列運算符(以所有形式,包括部分聚合和流不同)。

救助算法

這將我們帶入您問題的核心 - 救助算法究竟是做什麼的?它是“基於排序”還是基於“一種嵌套循環”?

可以說兩者兼而有之,這取決於您的觀點。當雜湊遞歸達到第 5 級時,記憶體中的雜湊分區從雜湊表變為雜湊值上最初為空的 b 樹索引。在 b-tree 索引中查找來自單個先前溢出的散列分區的每一行,並酌情插入(新組)或更新(維護聚合)。

這一系列對 b 樹的無序插入同樣可以視為插入排序或索引嵌套循環查找。

在任何情況下,這個回退算法都保證最終完成而不分配更多記憶體。如果可用於 b-tree 的空間不足以容納來自溢出分區的所有分組鍵和聚合,則可能需要多次傳遞。

一旦可用於保存 b-tree 索引的記憶體用完,任何進一步的行(來自目前溢出的分區)都將發送到單個新的tempdb分區(保證更小),並根據需要重複該過程。由於散列遞歸已經結束,溢出級別保持在 5。使用未記錄的跟踪標誌 7357 可以觀察到一些處理細節。

引用自:https://dba.stackexchange.com/questions/243132