關于sql的執行計劃(推薦詳細)
剛開始用SQL Server的時候,我沒有用顯示執行計劃來對查詢進行分析。我曾經一直認為我遞交的SQL查詢都是最優的,而忽略了查詢性能究竟如何,從而對“執行計劃”重視不夠。在我職業初期,我只要能獲取數據就很開心,而不去考慮數據是如何返回的,“執行計劃”對我的查詢作了什么工作。我以為SQL Server會自己去處理查詢的性能問題的。作為一個剛進入IT行業或者剛學到新技術的軟件工程師,在編寫代碼前不太可能有時間去學習其實必須掌握的知識。也許這是因為IT行業競爭太激烈的緣故。
隨著時間的流逝,數據庫容量慢慢變大了。終于某天,客戶對應用系統的查詢性能感到不滿意了。他面帶怒容來找我,抱怨由于查詢太慢,使得他需要花更多的時間來處理公務。最初,我建議客戶升級其系統資源,例如作為臨時解決方案,增加硬盤容量。雖然硬盤價格現在很便宜了,但是客戶還是要求我提供一個永久性的解決方案,檢查和好好調試查詢語句,來替代那種無休止地升級資源的臨時方案。因為客戶的滿意度對IT行業來說是十分重要的,因此我不得不考慮他的個人建議。我答應他一定會檢查和調整我的代碼。
如何入手呢?
在剛進入IT行業時,我知道SQL Server的基礎只是。說實話,向客戶承諾檢查系統的時候,我還沒有一點入手的頭緒。不過我相信我可以通過GOOGL和BOL來獲取相應的信息。
我閱讀了一些關于SQL Server的書籍,BOL,以及在網上搜索的信息。于是我知道了“顯示執行計劃”的概念。可以在查詢管理器中將該選項的開關設置為ON。“顯示執行計劃”是一個圖形化工具,可以幫助開發者和DBA分析,優化查詢,從而改善性能。
“顯示執行計劃”中不同的任務具有不同的圖標。本文中我主要對“Table Scan”、“Index Scan”、“Index Seek”、“Cluster Index Scan”以及“Clustered Index Seek”感興趣。也許在以后,可以對別的任務進行另外介紹。
時間以F1方程式的速度開始流逝,我覺得該是我全面理解“Table Scan”、“Index Scan”、“Index Seek”、“Clustered Index Scan”、和“Clustered Index Seek”如何工作的時候了。
我準備開始分析并優化我的查詢。在分析之前,我想到了一些問題。
- MS-SQL Server什么時候使用"Table Scan"?
- MS-SQL Server什么時候使用"Index Scan"?
- MS-SQL Server什么時候使用"Index Seek"?
- MS-SQL Server什么時候使用"Clustered Index Scan"?
- MS-SQL Server什么時候使用"Clustered Index Seek"?
?
我主要關注SQL Server是根據什么來使用“執行計劃”分析查詢的。在經過一段時間學習后,我了解了一些相關知識。這些知識應該對開發和DBA新手有幫助。于是我決定寫這篇文章,共享我的知識以幫助別人來理解“執行計劃”。
如果你喜歡,可以慢慢讀完,也可以在SQL Server上,模擬我下面做的實驗。
開始入手
為了解釋“顯示執行計劃”中的“Table Scan”、“Index Scan”、“Index Seek”、“Clustered Index Scan”和“Clustered Index Seek”,先創建新表,并添加一些示例數據進去。下面是創建新表的腳本:
?
Create Table PerformanceIssue |
?
表創建后需要添加一些數據。使用下面的腳本添加100,000條記錄進去。腳本執行時間可能比較長,請耐心等待其執行完畢。
?
Declare @Loop Int |
?
腳本成功執行后,數據就添加進去了。
用下面語句來看一下表的內容:
?
Select PRID, PRCode, PRDesc |
?
由于記錄較長,因此這里就不列出查詢結果了。
正如我前面講到,我想解釋何時會有“Table Scan”、“Index Scan”、“Index Seek”、“Clustered Index Scan”和“Clustered Index Seek”。上述哪個會改善性能呢?
當SQL Server返回數據時,我們想知道SQL Server采取何種掃描機制來協助獲取數據。首先看一下“Table Scan”。我們想了解什么時候“Table Scan”會產生。
選擇“顯示執行計劃”或者使用熱鍵“Alt + Q”來激活“顯示執行計劃”,當然也可以用快捷鍵“Ctrl+K”。
看一下執行下面查詢后的“執行計劃”結果。
?
Select PRID, PRCode, PRDesc |
上面的“執行計劃”中,SQL Server用到了“Table Scan”。我問自己為什么會有“Table Scan”,SQL Server是根據什么來使用該方法的。難道是因為我想獲取所有100,000條記錄嗎?于是我換了一個角度進行思考,如果來避免查詢中出現“Table Scan”呢?此時我對SQL Server的掃描機制還不是很清楚,那么該如何優化查詢呢?下面的SELECT查詢中僅選擇兩列:[PRID, PRCode]。
?
Select PRID, PRCode |
查詢執行后,執行計劃和第一個查詢一樣。于是將查詢改變為只檢索一個字段 [PRID]。
?
Select PRID |
查詢執行后,執行計劃仍然和第一個查詢的相同。對“Estimated row size”屬性不需要太大關注。意思我立刻決定只獲取一條記錄,看看執行計劃會如何。查詢語句如下:
?
Select PRID, PRCode, PRDesc |
?
執行完成后,執行計劃顯示:
查詢仍然使用了“Table Scan”方法來顯示數據。
那么,我需要想其它辦法來避免“Table Scan”。首先我想到應該給表加上索引。于是我在PRID字段上創建非聚集索引。添加了索引后是否就能避免“Table Scan”?下面我們開始討論關于“Index Scan”和“Index Seek”的主題。
Index Scan 和 Index Seek
首先在PRID字段上創建非聚集索引。
?
CREATE UNIQUE NONCLUSTERED INDEX UNC_PRID |
?
本文假定讀者已經知道非聚集索引如何工作的知識。了解非聚集索引更詳細的信息,請閱讀BOL相關主題,也可參看 http://www.sql-server-performance.com/gv_index_data_structures.asp。下面我們詳細講述“Index Scan”是如何工作的。
執行下面語句并查看執行計劃的結果。
?
Select PRID, PRCode, PRDesc |
奇怪了,“Table Scan”仍然用到了。為什么SQL Server沒有用到那個非聚集索引?于是繼續優化查詢語句,選擇檢索兩個字段 [PRID, PRCode] 。
?
Select PRID, PRCode From PerformanceIssue |
執行結果是和上一個查詢結果一摸一樣。于是修改查詢為只檢索一個字段 [PRID] 。
?
Select PRID |
?
執行計劃結果如下:
“Index Scan”在查詢中被用到了,這很好。很自然,接下來的問題就是“Index Scan”什么時候會被用到。字段PRID上有一個索引,查詢語句中選中的字段為PRID。執行查詢的時候,SQL Server掃描索引頁,因此用到了“Index Scan”方法。前面的查詢中選擇了有索引的和沒有索引的字段,SQL Server無法使用“Index Scan”。當查詢中只選擇有索引的字段時,SQL Server就使用了“Index Scan”。我不清楚SQL Server底層到底是如何判斷的,不過通過這些試驗,我認為當查詢中只選擇有索引的字段時,SQL Server就使用“Index Scan”方法。
下面看“Index Seek”方法何時產生。當我看到“Seek”這個詞時,第一反應就是條件查詢這個主意。
我嘗試三種不同的帶WHERE語法的查詢語句,以找出那種會用“Index Seek”。第一種語句如下:
?
Select PRID, PRCode, PRDesc |
?
結果顯示,執行計劃使用了“Table Scan”。
第二種語句如下:
?
Select PRID, PRCode, PRDesc |
?
執行計劃仍然使用“Table Scan”方法。
第三種查詢語句如下:
?
Select PRID, PRCode, PRDesc |
查詢用到了“Index Seek”和“Bookmark Lookup”方法。用到“Index Seek”是因為WHERE后面使用帶索引的字段PRID來進行過濾。“Bookmark Lookup”方法被用到是因為查詢中選擇了沒有索引的字段。如果去掉這兩個沒有索引的字段,那么“Bookmark Lookup”方法就可以去掉。當然如果只返回PRID字段,那么該查詢就沒什么意義了,因為WHERE語句后面已經給出PRID具體取值了。
我認為“Index Seek”在性能改善上比“Index Scan”和“Table Scan”要好,這主要表現在下面幾個方面:
?
- “Index Seek”不需要對表和索引頁進行掃描;而“Table Scan”和“Index Scan”需要。
- “Index Seek”利用“WHERE”來過濾獲取的數據,這樣比用“Index Scan”和“Table Scan”快很多。
?
當我完成這些測試后,我同事問我一個很有意思的問題:SQL Server什么時候使用“Clustered Index Scan”和“Clustered Index Seek”?下面對“Clustered Index Scan”和“Clustered Index Seek”進行實驗。
我決定在PRCode上建一個聚集索引來測試“Clustered Index Scan”和“Clustered Index Seek”。
Clustered Index Scan & Clustered Index Seek
下面的腳本刪除PRID字段上的索引,并在PRCode字段上創建聚集索引。
?
Drop Index PerformanceIssue.UNC_PRID |
?
關于聚集索引的基礎知識請查閱聯機幫助的相關主題或者 http://www.sql-server-performance.com/gv_index_data_structures.asp。下面我們將重點放在“Clustered Index Scan”和“Clustered Index Seek”如何被使用上。
執行下面查詢語句:
?
Select PRID, PRCode, PRDesc |
?
查詢執行后,可以看到執行計劃中用到了“Clustered Index Scan”。
下面用三種不同的WHERE方式來試驗何時SQL Server會用到“Clustered Index Seek”。第一種形式如下:
?
Select PRID, PRCode, PRDesc |
?
查詢執行后,可以看到執行計劃中用到了“Clustered Index Scan”。
第二種形式如下:
?
Select PRID, PRCode, PRDesc |
?
查詢執行后,發現執行計劃中用到的仍然是“Clustered Index Scan”。
第三種形式:
?
Select PRID, PRCode, PRDesc |
?
這次執行計劃用到了“Clustered Index Seek”。
當在WHERE后用到PRCode字段的時候,“Clustered Index Seek”被用到。執行計劃對聚集索引表檢索的時候,因為在選取的字段中,包括沒有索引的字段,所以不用用到“Bookmark Lookup”方法。
我個人認為,從改善性能角度考慮,“Clustered Index Seek”比“Clustered Index Scan”和“Index Seek”要好。
?
- “Clustered Index Seek”不需要掃描整個聚集索引頁。
- 和“Index Scan”相比,對于檢索選擇的字段包含那些沒有索引的字段時,“Clustered Index Seek”不會有“Bookmark Lookup”方法出現。
?
通過這些試驗,我對執行計劃的應用積累了實際經驗。我知道哪種掃描機制可以提高性能,從而是的客戶滿意。
?
posted on 2008-04-08 23:38 金家寶 閱讀(20340) 評論(8) 編輯 收藏 所屬分類: MS-SQL