關于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
          (
          ????PRID UniqueIdentifier NOT NULL,
          ????PRCode Int NOT NULL,
          ????PRDesc Varchar (100) NOT NULL
          )
          ON [PRIMARY]

          ?

          表創建后需要添加一些數據。使用下面的腳本添加100,000條記錄進去。腳本執行時間可能比較長,請耐心等待其執行完畢。

          ?

          Declare @Loop Int
          Declare @PRID UniqueIdentifier
          Declare @ PRDesc Varchar (100)

          Set @Loop = 1
          Set @ PRDesc = ''

          WHILE @Loop <= 100000
          BEGIN
          ?? Set @PRID = NewID()
          ?? Set @PRDesc = ' PerformanceIssue - ' + Convert( Varchar(10),@Loop )
          ?? Insert Into PerformanceIssue Values (@PRID, @Loop, @PRDesc)
          ?? Set @Loop = @Loop + 1
          END

          ?

          腳本成功執行后,數據就添加進去了。

          用下面語句來看一下表的內容:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          GO

          ?

          由于記錄較長,因此這里就不列出查詢結果了。

          正如我前面講到,我想解釋何時會有“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
          From PerformanceIssue
          GO


          上面的“執行計劃”中,SQL Server用到了“Table Scan”。我問自己為什么會有“Table Scan”,SQL Server是根據什么來使用該方法的。難道是因為我想獲取所有100,000條記錄嗎?于是我換了一個角度進行思考,如果來避免查詢中出現“Table Scan”呢?此時我對SQL Server的掃描機制還不是很清楚,那么該如何優化查詢呢?下面的SELECT查詢中僅選擇兩列:[PRID, PRCode]。

          ?

          Select PRID, PRCode
          From PerformanceIssue
          GO


          查詢執行后,執行計劃和第一個查詢一樣。于是將查詢改變為只檢索一個字段 [PRID]。

          ?

          Select PRID
          From PerformanceIssue

          GO


          查詢執行后,執行計劃仍然和第一個查詢的相同。對“Estimated row size”屬性不需要太大關注。意思我立刻決定只獲取一條記錄,看看執行計劃會如何。查詢語句如下:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          Where PRID = 'D386C151-5F74-4C2A-B527-86FEF9712955'
          -- PRID GUID value might be differ in your machine

          GO

          ?

          執行完成后,執行計劃顯示:

          查詢仍然使用了“Table Scan”方法來顯示數據。

          那么,我需要想其它辦法來避免“Table Scan”。首先我想到應該給表加上索引。于是我在PRID字段上創建非聚集索引。添加了索引后是否就能避免“Table Scan”?下面我們開始討論關于“Index Scan”和“Index Seek”的主題。


          Index Scan 和 Index Seek

          首先在PRID字段上創建非聚集索引。

          ?

          CREATE UNIQUE NONCLUSTERED INDEX UNC_PRID
          ON PerformanceIssue (PRID)
          GO

          ?

          本文假定讀者已經知道非聚集索引如何工作的知識。了解非聚集索引更詳細的信息,請閱讀BOL相關主題,也可參看 http://www.sql-server-performance.com/gv_index_data_structures.asp。下面我們詳細講述“Index Scan”是如何工作的。

          執行下面語句并查看執行計劃的結果。

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          GO


          奇怪了,“Table Scan”仍然用到了。為什么SQL Server沒有用到那個非聚集索引?于是繼續優化查詢語句,選擇檢索兩個字段 [PRID, PRCode] 。

          ?

          Select PRID, PRCode From PerformanceIssue
          GO


          執行結果是和上一個查詢結果一摸一樣。于是修改查詢為只檢索一個字段 [PRID] 。

          ?

          Select PRID
          From PerformanceIssue
          GO

          ?

          執行計劃結果如下:

          “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
          From PerformanceIssue
          Where PRCode = 8
          GO

          ?

          結果顯示,執行計劃使用了“Table Scan”。

          第二種語句如下:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          Where PRDesc = ' PerformanceIssue - 8'
          GO

          ?

          執行計劃仍然使用“Table Scan”方法。

          第三種查詢語句如下:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          Where PRID = 'D386C151-5F74-4C2A-B527-86FEF9712955'

          -- PRID GUID value might be differ in your machine
          GO



          查詢用到了“Index Seek”和“Bookmark Lookup”方法。用到“Index Seek”是因為WHERE后面使用帶索引的字段PRID來進行過濾。“Bookmark Lookup”方法被用到是因為查詢中選擇了沒有索引的字段。如果去掉這兩個沒有索引的字段,那么“Bookmark Lookup”方法就可以去掉。當然如果只返回PRID字段,那么該查詢就沒什么意義了,因為WHERE語句后面已經給出PRID具體取值了。

          我認為“Index Seek”在性能改善上比“Index Scan”和“Table Scan”要好,這主要表現在下面幾個方面:

          ?

          1. “Index Seek”不需要對表和索引頁進行掃描;而“Table Scan”和“Index Scan”需要。
          2. “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
          GO
          CREATE UNIQUE CLUSTERED INDEX UC_PRCode
          ON PerformanceIssue( PRCode)
          GO
          -------------
          Clustered index has been created successfully.
          Index has been created.

          ?

          關于聚集索引的基礎知識請查閱聯機幫助的相關主題或者 http://www.sql-server-performance.com/gv_index_data_structures.asp。下面我們將重點放在“Clustered Index Scan”和“Clustered Index Seek”如何被使用上。

          執行下面查詢語句:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          GO

          ?

          查詢執行后,可以看到執行計劃中用到了“Clustered Index Scan”。

          下面用三種不同的WHERE方式來試驗何時SQL Server會用到“Clustered Index Seek”。第一種形式如下:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          Where PRDesc = ' PerformanceIssue - 8'
          GO

          ?

          查詢執行后,可以看到執行計劃中用到了“Clustered Index Scan”。

          第二種形式如下:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          Where PRID = 'D386C151-5F74-4C2A-B527-86FEF9712955'

          -- PRID GUID value might be differ in your machine
          GO

          ?

          查詢執行后,發現執行計劃中用到的仍然是“Clustered Index Scan”。

          第三種形式:

          ?

          Select PRID, PRCode, PRDesc
          From PerformanceIssue
          Where PRCode = 8
          GO

          ?

          這次執行計劃用到了“Clustered Index Seek”。

          當在WHERE后用到PRCode字段的時候,“Clustered Index Seek”被用到。執行計劃對聚集索引表檢索的時候,因為在選取的字段中,包括沒有索引的字段,所以不用用到“Bookmark Lookup”方法。

          我個人認為,從改善性能角度考慮,“Clustered Index Seek”比“Clustered Index Scan”和“Index Seek”要好。

          ?

          1. “Clustered Index Seek”不需要掃描整個聚集索引頁。
          2. 和“Index Scan”相比,對于檢索選擇的字段包含那些沒有索引的字段時,“Clustered Index Seek”不會有“Bookmark Lookup”方法出現。

          ?

          通過這些試驗,我對執行計劃的應用積累了實際經驗。我知道哪種掃描機制可以提高性能,從而是的客戶滿意。

          ?

          posted on 2008-04-08 23:38 金家寶 閱讀(20353) 評論(8)  編輯  收藏 所屬分類: MS-SQL

          評論

          # re: 關于sql的執行計劃(推薦詳細)[未登錄] 2012-10-16 16:51 candy

          “因為在選取的字段中,包括沒有索引的字段,所以不用用到“Bookmark Lookup”方法” 這句話是錯的

          因為在有聚集索引的表中根據聚集索引檢索數據,索引中已經包含了該條數據的全部內容,所以才不需要Bookmark Lookup  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細) 2013-01-22 17:05 張俊

          好東東  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細)[未登錄] 2013-05-15 18:08 Frank

          很少看到寫得這么細致的文章,掌聲鼓勵!  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細) 2013-07-22 15:36 很郁悶

          什么是BOL?是BLOG嗎?  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細) 2014-04-03 16:28 niaomingjian

          @candy
          有道理  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細) 2015-03-21 14:22 王振

          @niaomingjian
          說的很好  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細)[未登錄] 2015-04-13 17:39 lee

          2.“Index Seek”利用“WHERE”來過濾獲取的數據,這樣比用“Index Scan”和“Table Scan”快很多。 個人覺得這句話表達意思不對,不建立索引的時候,也可以用WHERE來過濾獲取數據。  回復  更多評論   

          # re: 關于sql的執行計劃(推薦詳細)[未登錄] 2015-06-18 15:17 A

          內容(請不要發表任何與政治相關的內容)   回復  更多評論   

          主站蜘蛛池模板: 政和县| 安新县| 库尔勒市| 白沙| 永善县| 华池县| 新建县| 金溪县| 教育| 苏尼特右旗| 武安市| 杨浦区| 当涂县| 衢州市| 阿城市| 阳东县| 远安县| 青阳县| 闸北区| 治多县| 资兴市| 慈溪市| 哈巴河县| 永仁县| 资溪县| 延长县| 磐安县| 平利县| 玉山县| 布尔津县| 新干县| 泸西县| 新平| 靖宇县| 武陟县| 九龙县| 临夏县| 洪泽县| 聊城市| 临颍县| 常熟市|