1. 程式人生 > >翻譯(十一) ——Reading Query Plans: Stairway to SQL Server Indexes Level 9

翻譯(十一) ——Reading Query Plans: Stairway to SQL Server Indexes Level 9

排序效率 left 成本 asp ima 選項卡 一起 create 支持

Reading Query Plans: Stairway to SQL Server Indexes Level 9

By David Durant, 2011/10/05

原文鏈接:http://www.sqlservercentral.com/articles/Stairway+Series/72441/

The Series

本文是階梯系列的一部分:SQL Server索引的階梯。

索引是數據庫設計的基礎,並告訴開發人員使用數據庫大量關於設計者的意圖。不幸的是,當性能問題出現時,索引常常會作為事後考慮添加。這裏最後是一系列簡單的文章,應該能讓數據庫專業人員快速地與它們同步。

在整個步驟中,我們經常聲明某個查詢以某種方式執行;我們引用生成的查詢計劃來支持我們的語句。管理庫顯示估計和實際查詢計劃,可以幫助您確定索引的好處或不足。因此,這個級別的目的是讓您對查詢計劃有足夠的了解:

當你閱讀這個階梯時,驗證我們的斷言。

確定索引是否有利於查詢。

有許多文章閱讀查詢計劃,包括MSDN Library中的幾個。我們不打算擴展或替換它們。事實上,我們將在這個級別為他們中的許多人提供鏈接/引用。開始的好地方是顯示圖形執行計劃。(http://msdn.microsoft.com/en-us/library/ms178071.aspx). 其他有用的資源包括Grant Fritchey的書,SQL執行計劃(可用於電子書免費),和Fabiano Amorim的一系列簡單的對話篇關於各運營商發現您的查詢計劃的輸出(http://www.simple-talk.com/author/fabiano-amorim/).

Graphical Query Plans

查詢計劃是SQLServer執行查詢時所遵循的一組指令。SQLServer將以文本、圖形或XML格式顯示對您的查詢計劃。例如,考慮下面的簡單查詢:

    SELECT LastName, FirstName, MiddleName, Title 

FROM Person.Contact

WHERE Suffix = ‘Jr.‘

ORDER BY Title

這個查詢的計劃可以如圖1所示。

技術分享圖片

1 -圖形格式的實際查詢計劃

或者,可以將其視為文本:

|--Sort(ORDER BY:([AdventureWorks].[Person].[Contact].[Title] ASC))


|--Clustered Index
Scan(OBJECT:([AdventureWorks].[Person].[Contact].[PK_Contact_ContactID]),
WHERE:([AdventureWorks].[Person].[Contact].[Suffix]=N‘Jr.‘))

或者作為一個XML文檔,從這樣開始:

技術分享圖片

查詢計劃的顯示可以如下所示要求:

要請求圖形化查詢計劃,請使用管理室的SQL編輯器工具欄,該工具欄同時具有“顯示估算執行計劃”和“包含實際執行計劃”按鈕。“顯示估計的執行計劃”選項顯示查詢計劃圖立即為選定的TSQL代碼,而不執行查詢。“包含實際執行計劃”按鈕是一個開關,一旦您選擇了這個選項,您執行的每個查詢批將在一個新選項卡中顯示查詢計劃圖,以及結果和消息。這個選項可以在圖1中看到。

要求文本查詢計劃,使用設置showplan_text聲明。打開文本版本將關閉圖形版本,不會執行任何查詢。

要查看XML版本,在圖形版本中右擊,並從上下文菜單中選擇“顯示執行計劃XML”。

對於這個級別的其余部分,我們專註於圖形視圖,因為它通常提供對計劃的最快的理解。對於查詢計劃,一個圖片通常比一千個詞更好。

Reading Graphical Query Plans

圖形查詢計劃通常從右向左讀取;右最圖標表示數據收集流中的第一步。這通常是訪問堆或索引。您不會看到這裏使用的字表;相反,您將看到集群索引掃描或堆掃描。這是第一個查看哪些索引(如果有的話)正在使用的地方。

圖形化查詢計劃中的每個圖標表示一個操作。在可能的圖標的更多信息,參見圖形執行計劃在http://msdn.microsoft.com/en-us/library/ms175913.aspx圖標

連接操作的箭頭表示行,從一個操作流出並進入下一個操作。

將鼠標放在圖標或箭頭上會導致顯示更多信息。

不要把操作看作步驟,因為這意味著在下一個操作開始之前必須完成一個操作。這不一定是真的。例如,當對WHERE子句進行求值時,也就是說,當執行篩選器操作時,一次一行求值,而不是一次求值。在後續行到達篩選器操作之前,一行可以移動到下一操作。另一方面,排序操作必須在第一行移動到下一操作之前全部完成。

Using Some Additional Information

圖形化查詢計劃顯示了兩個可能有用的信息,這些信息不是計劃本身的一部分;建議的索引和每個操作的相對成本。

在上面的例子中,所提出的指標,顯示綠色和空間要求截斷,建議聯系表的後綴列的非聚集索引;包括標題、FirstNameMiddleName列,和姓。

這個計劃的每個操作的相對成本告訴我們,排序操作占總成本的5%,而表掃描是工作的95%。因此,如果我們想改進這個查詢的性能,我們應該處理表掃描,而不是排序,這就是為什麽建議索引的原因。如果我們創建推薦索引,像這樣:

CREATE NONCLUSTERED INDEX IX_Suffix ON Person.Contact
(
Suffix
)
INCLUDE ( Title, FirstName, MiddleName, LastName )

然後重新運行查詢,我們的讀取量從569下降到3;新的查詢計劃如下所示。

技術分享圖片

新的非聚集索引,其索引鍵後綴,有“後綴=Jr.”條目聚集在一起;因此,在IO需要檢索數據還原。因此,排序操作與前一個計劃中的排序操作相同,現在代表了查詢總成本的75%以上,而不是原來的5%。因此,原始計劃需要75/5倍於當前計劃收集相同信息的工作量的15倍。

由於我們的WHERE子句只包含一個相等運算符,因此我們可以通過將標題列移動到索引鍵來改進索引:

IF  EXISTS (SELECT * FROM sys.indexes
WHERE OBJECT_ID = OBJECT_ID(NPerson.Contact)

AND name = NIX_Suffix)
DROP INDEX IX_Suffix ON Person.Contact
CREATE NONCLUSTERED INDEX IX_Suffix ON Person.Contact
(
Suffix, Title
)
INCLUDE ( FirstName, MiddleName, LastName )

現在,所需的條目仍然在索引中聚集在一起,並且在每個集群中都在請求的序列中;如新的查詢計劃所示,如圖2所示。

技術分享圖片

2查詢計劃重建非聚簇索引後

現在計劃表明不再需要排序操作。在這一點上,我們可以放棄高收益的覆蓋指數。這將使聯系人表恢復到我們開始時的狀態;當我們進入下一個話題時,這是我們希望它處於的狀態。

Viewing Parallel Streams

如果可以並行處理兩行行,它們將在圖形顯示中彼此上下顯示。箭頭的相對寬度指示通過每個流處理的行數。

例如,下面的連接將前面的查詢擴展為包含銷售信息:

SELECT C.LastName, C.FirstName, C.MiddleName, C.Title
, H.SalesOrderID, H.OrderDate
FROM Person.Contact C
JOIN Sales.SalesOrderHeader H ON H.ContactID = C.ContactID
WHERE Suffix = Jr.
ORDER BY Title

查詢計劃如圖3所示。

技術分享圖片

3 -連接的查詢計劃

快速看一下這個計劃會告訴我們一些事情:

兩個表同時掃描。

大部分的工作都花在掃描桌子上。

多排出來或salesorderheader表超過了接觸表。

兩表不聚為同一序列;因此每個salesorderheader行行匹配的接觸將需要額外的努力。在這種情況下,使用哈希匹配操作。(稍後再討論散列)。

排序所選行所需的努力可以忽略不計。

即使是單獨的行行也可以分解成單獨的行,以利用並行處理。例如,如果我們將上面查詢中的WHERE子句改為後綴為null的地方。

將返回更多的行,因為95%的聯系人行有一個空後綴。新的查詢計劃反映了這一點,如圖4所示。

技術分享圖片

4 -一個並行查詢計劃

新計劃還向我們表明,接觸行數的增加導致匹配和排序操作成為該查詢的關鍵路徑。如果我們需要提高它的性能,我們必須首先攻擊這兩種操作。同樣,包含列的索引將有助於。

和大多數連接一樣,我們的示例通過外鍵/主鍵關系連接兩個表。其中的一個表,聯系,是先由ContactID,這也正好是其主要的關鍵。在其他表中,saleorderheader ContactID是外鍵。自從ContactID是外鍵,用於數據訪問的saleorderheader ContactID請求,如我加入的例子,可能是一種常見的業務需求。這些請求將從索引ContactID效益。

每當您對外鍵列進行索引時,總是問自己,如果列中包含了列,那麽應該添加哪些索引。在我們的例子中,我們只有一個查詢,而不是一個支持查詢的家庭。因此,我們只包括將OrderDate列。支持一個家庭的ContactID導向對saleorderheader表查詢,我們將包括在索引中,列的更多saleorderheader的需要,以支持那些額外的查詢。

我們的創建索引語句是:

CREATE NONCLUSTERED INDEX IX_ContactID ON Sales.SalesOrderHeader
(
ContactID
)
INCLUDE ( OrderDate )

而新計劃的執行我們的salesorderheader和聯系信息加入如圖5

技術分享圖片

5 -計劃在每個表上有一個支持索引的連接查詢

由於輸入流現在是由謂詞連接柱、ContactID;測序加入部分的查詢可以做到不裂流無散列;從而減少26 + 5 + 3 = 34%的工作負載下4%的工作負載是什麽。

Sorting, Presorting and Hashing

許多查詢操作要求在執行操作之前將數據分組。這些包括明確的,聯盟(這意味著不同),按組(及其各種聚合函數),並加入。通常,SQL Server將使用三種方法中的一種來實現這種分組,第一種方法需要您的幫助:

高興地發現數據已經被預設為分組序列。

通過執行哈希操作將數據分組。

將數據排序到分組序列中。

Presorting

指標是你的預存數據;即在經常需要對SQL Server提供的數據序列。這就是為什麽非聚集索引的創建,每個包含包含性列,得益於我們之前的例子。事實上,如果將鼠標放在最近查詢中的合並連接圖標上,則短語將使用兩個適當排序的輸入流匹配行,利用它們的排序順序。將出現。這將告訴您兩個表/索引的行是使用內存和處理器時間的絕對最小值來連接的。相應排序的輸入是一個精彩的短語看到當鼠標查詢計劃的圖標,它驗證了你選擇的指標。

Hashing

如果輸入的數據不是理想的序列,SQLServer可以使用哈希操作對數據分組。哈希是一種可以使用大量內存的技術,但通常比排序效率更高。當執行不同,聯盟,加入行動,散列在單個行可以通過下一步操作無需等待所有的輸入行被哈希排序優勢。但是,在計算分組聚集時,必須在將所有聚合值傳遞給下一個操作之前讀取所有輸入行。

散列信息所需的內存量與所需的組數直接相關。因此需要解析散列:

SELECT Gender, COUNT(*)
FROM NewYorkCityCensus
GROUP BY Gender
只需要很少的內存,因為只有兩個組:女性和男性,不管輸入行數。另一方面:
SELECT LastName, FirstName, COUNT(*)
FROM NewYorkCityCensus
GROUP BY LastName, FirstName

將導致大量的組,每個組都需要在內存中擁有自己的空間;可能消耗了太多的內存,哈希成為解決查詢的不可取的技術。

更多的查詢計劃哈希,訪問http://msdn.microsoft.com/en-us/library/ms189582.aspx

Sorting

如果數據沒有預設(索引),如果SQL Server認為散列不能高效完成,SQL Server將數據排序。這通常是最不可取的選擇。因此,如果在計劃早期出現排序圖標,請檢查是否可以改進索引。如果Sorticon出現在計劃結束時,這可能意味著SQL ServerORDER BY子句的請求序列排序的最終輸出;這種序列與序列被用來解決查詢的連接、分組、和工會。通常,在這一點上你可以做些什麽來避免這種排序。

Conclusion

查詢計劃顯示SQL Server打算使用或使用的方法來執行查詢。它通過詳細說明將要使用的操作、從操作到操作的行流以及所涉及的並行性。

您將此信息視為文本、圖形或XML顯示。

圖解顯示每個操作的相對工作負荷。

圖形化計劃可能會提供一個索引來提高查詢的性能。

理解查詢計劃有助於評估和優化索引設計。

翻譯(十一) ——Reading Query Plans: Stairway to SQL Server Indexes Level 9