前一篇文章提到了在2014下分區表引起的奇葩問題,最近也在苦苦研究問題的原因。這篇文章主要講述在分區表下,查詢是怎麼樣的一個情況。由於2014的新功能“參數估計”下是如何運轉的目前無從得知,所以只能分享下2008下的原理了... 在 SQL Server 2008 中,已分區表的內部表示形式已發生變 ...
前一篇文章提到了在2014下分區表引起的奇葩問題,最近也在苦苦研究問題的原因。這篇文章主要講述在分區表下,查詢是怎麼樣的一個情況。由於2014的新功能“參數估計”下是如何運轉的目前無從得知,所以只能分享下2008下的原理了...
在 SQL Server 2008 中,已分區表的內部表示形式已發生變化,即已分區表將作為一個多列索引呈現給查詢處理器,其中 PartitionID是第一列。PartitionID 是一個隱藏的計算列,用於在內部表示包含特定行的分區的 ID。例如,假設一個定義為 T(a, b, c) 的表 T 在 a列進行了分區,併在 b 列的聚集索引。在 SQL Server 2008 中,此分區表在內部被視為一個具有架構 T(PartitionID, a, b, c) 的未分區表,並具有組合鍵 (PartitionID, b) 的聚集索引。這樣查詢優化器便可以基於 PartitionID 對任何已分區表或索引執行查找操作。
此外,查詢優化器的功能也得以擴展,可以針對 PartitionID(作為邏輯首列)以及其他可能的索引鍵列執行某一條件下的查找或掃描操作,然後,對於符合第一級查找操作的條件的每個不同值,再針對一個或多個其他列執行不同條件下的二級查找。也就是說,這種稱為“skip-scan”的操作允許查詢優化器基於某一條件來執行查找或掃描操作以確定要訪問的分區,然後在該運算符內執行一個二級索引查找操作以返回這些分區中符合另一個不同條件的行。例如,請考慮以下查詢。
SELECT * FROM T WHERE a < 10 and b = 2;
對於本示例,假設定義為 T(a, b, c) 的表 T 對 a 列進行了分區,並具有 b 的聚集索引。表 T 的分區邊界由以下分區函數定義:
CREATE PARTITION FUNCTION myRangePF1 (int) AS RANGE LEFT FOR VALUES (3, 7, 10);
為求解該查詢,查詢處理器將執行第一級查找操作以查找包含符合條件 T.a < 10 的行的每個分區。這將標識要訪問的分區。然後,在所標識的每個分區內,處理器將針對 b 列的聚集索引執行一個二級查找以查找符合條件 T.b = 2 和 T.a < 10 的行。
下圖所示為跳躍掃描操作的邏輯表示形式,其中顯示了在 a 列和 b 列中包含數據的表 T。分區編號為 1 到 4,分區邊界由垂直虛線表示。對分區執行的第一級查找操作(圖中未顯示)已確定分區 1、2 和 3 符合查找條件(由為該表定義的分區和 a 列的謂詞指示),即T.a < 10。曲線指示了跳躍掃描操作的二級查找部分所遍歷的路徑。實際上,跳躍掃描操作將在這些分區的每個分區中查找符合條件 b = 2 的行。跳躍掃描操作的總開銷等於三個單獨索引查找之和。
-------已分區堆的執行計劃------------------------------------------------------
已分區堆被視為分區 ID 的邏輯索引。已分區堆的分區排除在執行計劃中表示為一個 Table Scan 運算符,其中對分區 ID 使用了 SEEK 謂詞。下麵的示例顯示了所提供的顯示計劃信息:
|-- Table Scan (OBJECT: ([db].[dbo].[T]), SEEK: ([PtnId1001]=[Expr1011]) ORDERED FORWARD)
------歸置聯接的執行計劃--------------------------------------------------------
使用相同或等效的分區函數對兩個表進行分區並且在查詢的聯接條件中指定了來自聯接兩側的分區依據列時就會發生聯接歸置。查詢優化器可以生成一個計劃,其中具有相等分區 ID 的每個表的分區將分別聯接在一起。歸置聯接可能比非歸置聯接的執行速度快,因為前者可以只需較少的記憶體和處理時間。優化器會基於成本估計來選擇非歸置計劃或歸置計劃。
在歸置計劃中,Nested Loops 聯接從內側讀取一個或多個聯接表或索引分區。Constant Scan 運算符內的數字表示分區號。
為已分區表或索引生成歸置聯接的並行計劃時,在 Constant Scan 和 Nested Loops 聯接運算符之間會出現一個 Parallelism 運算符。在此情況下,在聯接外側的多個線程會各自在不同的分區上進行讀取和操作。
下圖顯示了一個歸置聯接的並行查詢計劃。
------並行查詢執行策略--------------------------------------------------------
查詢處理器對從已分區對象選擇的查詢使用查詢執行策略。作為執行策略的一部分,查詢處理器會確定查詢所需的表分區,以及要分配給每個分區的線程比例。在大多數情況下,查詢處理器會為每個分區分配數量相等或幾乎相等的線程,然後在這些分區中並行地執行查詢。以下幾段更詳細地介紹了線程分配情況。
如果線程數小於分區數,則查詢處理器會將每個線程分配給一個不同的分區,最初會有一個或多個分區沒有獲得分配的線程。當線程完成在一個分區上的執行時,查詢處理器會將它分配給下一個分區,直到每個分區都分配有一個線程。這是查詢處理器將線程重新分配給其他分區的唯一情況。
如果線程數與分區數相等,則查詢處理器會為每個分區分配一個線程。當線程完成時,不會重新分配給另一個分區。
如果線程數大於分區數,則查詢處理器會為每個分區分配相等數量的線程。如果線程數並非恰好是分區數的倍數,則查詢處理器會為某些分區額外分配一個線程,以使用所有可用線程。請註意,如果只有一個分區,則會將所有線程都分配給該分區。在下圖中,有四個分區和 14 個線程。每個分區都分配有 3 個線程,兩個分區具有一個額外的線程,總共分配了 14 個線程。當線程完成時,不會重新分配給另一個分區。
儘管以上示例指出了一種分配線程的簡單方式,但實際策略要複雜一些,並需要考慮在查詢執行過程中出現的其他變化因素。例如,如果表已分區,併在 A 列上有一個聚集索引,並且查詢有謂詞子句 WHERE A IN (13, 17, 25),則查詢處理器將為這三個查找值(A=13、A=17 和 A=25))各分配一個或多個線程,而不是為每個表分區分配一個或多個線程。只需在包含這些值的分區中執行查詢,並且如果所有這些查找謂詞都恰好在同一個表分區中,則所有線程都將分配給同一個表分區。
為了舉出另一個示例,假定表在 A 列上有四個分區(邊界點為 (10, 20, 30)),在 B 列上有一個索引,並且查詢有一個謂詞子句WHERE B IN (50, 100, 150)。因為表分區是基於值 A,所以值 B 可以出現在任何表分區中。這樣,查詢處理器將分別在四個表分區中查找三個 B 值 (50, 100, 150) 中的每一個值。查詢處理器將按比例分配線程,以便它可以並行執行 12 個查詢掃描中的每一個掃描。
基於 A 列的表分區 |
在每個表分區中查找 B 列 |
---|---|
表分區 1:A < 10 |
B=50, B=100, B=150 |
表分區 2:A >= 10 AND A < 20 |
B=50, B=100, B=150 |
表分區 3:A >= 20 AND A < 30 |
B=50, B=100, B=150 |
表分區 4:A >= 30 |
B=50, B=100, B=150 |