剖析SQL Server执行计划
-->Title: 淺議SQL Server執(zhí)行計(jì)劃
-->Author: wufeng4552
-->Date :2009-10-20 15:08:24
前言:
最近溫習(xí)了執(zhí)行計(jì)劃方面的部份知識(shí),為了加深印象與方便初學(xué)者,特做了如下整理.
不對(duì)地方歡迎提出並指正.
查看執(zhí)行計(jì)劃的方式:
(1)菜單方式:
(1.1)顯示實(shí)際執(zhí)行計(jì)劃
(1.2)顯示預(yù)估的執(zhí)行計(jì)劃
以上兩種均位於位于”查詢”下拉菜單中,兩者的不同之處在于當(dāng)實(shí)際運(yùn)行一個(gè)查詢時(shí),當(dāng)前的服務(wù)器上的運(yùn)算也會(huì)被考慮進(jìn)去。大多數(shù)情況下,兩種方式產(chǎn)生的執(zhí)行計(jì)劃產(chǎn)生的結(jié)果是相似的.
(2)命令方式
SET SHOWPLAN_TEXT ON
這條命令被執(zhí)行后,所有在當(dāng)前這個(gè)查詢分析器會(huì)話中執(zhí)行的查詢都不會(huì)運(yùn)行,而是會(huì)顯示一個(gè)基于文本的執(zhí)行計(jì)劃
注意:執(zhí)行某條用到臨時(shí)表的查詢時(shí),必須在執(zhí)行查詢先運(yùn)行SET STATISTICS PROFILE ON語(yǔ)句 如:
go
if not object_id('tempdb..#t') is null
?drop table #t
Go
Create table #t([日期] Datetime,[姓名] nvarchar(2))
Insert #t
select '2009-10-01',N'張三' union all
select '2009-10-01',N'李四' union all
select '2009-10-02',N'趙六'
Go
SET STATISTICS PROFILE ON
go
select * from #T
SET STATISTICS PROFILE OFF
結(jié)果如圖1
?
圖1
為了討論方便 下面以 Northwind 庫(kù)中表 [Order Details] 為例(我已經(jīng)將主鍵刪除)
use Northwind
go
SET SHOWPLAN_TEXT ON
go
select ProductID,sum(Quantity)Quantity from [Order Details]
group by ProductID order by ProductID
go
SET SHOWPLAN_TEXT OFF
/*
StmtText
|--Sort(ORDER BY:([Northwind].[dbo].[Order Details].[ProductID] ASC))
???? |--Hash Match(Aggregate, HASH:([Northwind].[dbo].[Order Details].[ProductID]) DEFINE:([Expr1004]=SUM([Northwind].[dbo].[Order Details].[Quantity])))
??????? |--Table Scan(OBJECT:([Northwind].[dbo].[Order Details]))
*/
use Northwind
go
----建一個(gè)聚集索引
CREATE CLUSTERED INDEX INDEX_ProductID on [Order Details](ProductID)
go
SET SHOWPLAN_TEXT ON
go
select ProductID,sum(Quantity)Quantity from [Order Details]
group by ProductID order by ProductID
go
SET SHOWPLAN_TEXT OFF
/*
StmtText
---------------------------------------
? |--Stream Aggregate(GROUP BY:([Northwind].[dbo].[Order Details].[ProductID]) DEFINE:([Expr1004]=SUM([Northwind].[dbo].[Order Details].[Quantity])))
?????? |--Clustered Index Scan(OBJECT:([Northwind].[dbo].[Order Details].[INDEX_ProductID]), ORDERED FORWARD)
(2 個(gè)資料列受到影響)
*/
如果在執(zhí)行計(jì)劃中看到如下所示的任何一項(xiàng),從性能方面來(lái)說(shuō),下面所示的每一項(xiàng)都是不理想的。
Index or table scans(索引或者表掃描):可能意味著需要更好的或者額外的索引。
Bookmark Lookups(書(shū)簽查找):考慮修改當(dāng)前的聚集索引,使用復(fù)蓋索引,限制SELECT語(yǔ)句中的字段數(shù)量。
Filter(過(guò)濾):在WHERE從句中移除用到的任何函數(shù),不要在SQL語(yǔ)句中包含視圖,可能需要額外的索引。
Sort(排序):數(shù)據(jù)是否真的需要排序?可否使用索引來(lái)避免排序?在客戶端排序是否會(huì)更加有效率?
以上事項(xiàng)避免得越多,查詢性能就會(huì)越快.
注意:如果有在存儲(chǔ)過(guò)程中或者其它T-SQL批處理代碼中用到了臨時(shí)表,就不能在查詢分析器或Management Studio使用”顯示預(yù)估的執(zhí)行計(jì)劃”選項(xiàng)來(lái)評(píng)估查詢。必須實(shí)際運(yùn)行這個(gè)存儲(chǔ)過(guò)程或者批處理代碼。這是因?yàn)槭褂谩憋@示預(yù)估的執(zhí)行計(jì)劃”選項(xiàng)來(lái)運(yùn)行一個(gè)查詢時(shí),它并沒(méi)有實(shí)際被運(yùn)行,臨時(shí)表也沒(méi)有創(chuàng)建。由于臨時(shí)表沒(méi)有被創(chuàng)建,參考到臨時(shí)表的代碼就會(huì)失敗,導(dǎo)致預(yù)估的執(zhí)行計(jì)劃不能成創(chuàng)建成功。從另一方面來(lái)說(shuō),如果使用的是表變量而不是臨時(shí)表,則可以使用”顯示預(yù)估的執(zhí)行計(jì)劃”選項(xiàng).
use Northwind
go
select a.* from [orders] a,[Order Details] b
where a.OrderID=b.OrderID
?
圖2
查看執(zhí)行計(jì)劃時(shí)記住如下幾點(diǎn):
(1)???? 非常復(fù)雜的執(zhí)行計(jì)劃會(huì)被分成多個(gè)部分,它們分別列出在屏幕上。每個(gè)部分分別代表查詢優(yōu)化器為了得到最終結(jié)果而必須執(zhí)行的單個(gè)處理或步驟。執(zhí)行計(jì)劃的每個(gè)步驟經(jīng)常會(huì)被拆分成一個(gè)個(gè)更小的子步驟。不幸的是,它們是從右至左顯示在屏幕上的。這意味著你必須滾動(dòng)到圖形執(zhí)行計(jì)劃的最右邊去查看每個(gè)步驟是從哪兒開(kāi)始的
(2)???? 每個(gè)步驟與子步驟間通過(guò)箭頭連接,藉此顯示查詢執(zhí)行的路徑。
(3)???? 最后,查詢的所有部分在屏幕頂部的左邊匯總到一起,如果將鼠標(biāo)移動(dòng)到連接步驟或子步驟的箭頭上,就可以看到一個(gè)彈出式窗口,上面顯示有多少筆記錄從一個(gè)步驟或子步驟移動(dòng)到另一個(gè)步驟或子步驟(如圖3) 如果將鼠標(biāo)移動(dòng)到任何執(zhí)行計(jì)劃任何步驟或者子步驟的上面,就會(huì)顯示一個(gè)彈出式窗口,上面顯示該步驟或子步驟的更加詳細(xì)的信息如圖2彈出窗口
?
圖3
(4)???? 圖形執(zhí)行計(jì)劃上連接每個(gè)圖標(biāo)的箭頭粗細(xì)不同(如圖3)。箭頭的粗細(xì)表示每個(gè)圖標(biāo)之間移動(dòng)的數(shù)據(jù)行數(shù)量以及數(shù)據(jù)行大小移動(dòng)所需的相對(duì)本。箭頭越粗,相對(duì)成本就越高。可以使用這個(gè)指示器來(lái)快速測(cè)量一個(gè)查詢。你可能會(huì)特別關(guān)注粗箭頭以了解它如何影響到查詢的效能。例如,粗線頭應(yīng)該在圖形執(zhí)行計(jì)劃的右邊,而非左邊。如果看到它們?cè)谧筮?#xff0c;就意味著太多的數(shù)據(jù)行被返回,這個(gè)執(zhí)行計(jì)劃也不是最佳的執(zhí)行計(jì)劃.
(5)???? 執(zhí)行計(jì)劃的每個(gè)部分都被分配了一個(gè)成本百分比(如圖2,3)。它表示這個(gè)部分耗用了整個(gè)執(zhí)行計(jì)劃的多少資源。當(dāng)對(duì)一個(gè)執(zhí)行計(jì)劃進(jìn)行分析的時(shí)候,應(yīng)該將精力集中于有著高成本百分比的那些部分。這樣就可以在有限的時(shí)間里找到可能性最大的問(wèn)題,從而回報(bào)了你在時(shí)間上的投資.
(6)???? 你可能會(huì)注意到一個(gè)執(zhí)行計(jì)劃的某些部分被執(zhí)行了不止一次。作為執(zhí)行計(jì)劃分析的一部分,應(yīng)該將你的一些時(shí)間集中在任何執(zhí)行了超過(guò)一次的那些部分上,看看是否有什么方式減少它們執(zhí)行的次數(shù)。執(zhí)行的次數(shù)越少,查詢的速度就越快。
(7)???? I/O與CPU成本。查詢優(yōu)化器使用這些數(shù)字來(lái)做出最佳選擇。它們可用來(lái)參考的一個(gè)意義是,較小的I/O或CPU成本比較大的I/O或CPU成本使用更少的服務(wù)器資源.
use Northwind
go
--刪除聚集索引
DROP INDEX [Order Details].INDEX_ProductID
--CREATE CLUSTERED INDEX INDEX_ProductID ON [Order Details](ProductID)
SET STATISTICS IO ON
select * from [Order Details] where ProductID=42
SET STATISTICS IO OFF
資料表'Order Details'。掃描計(jì)數(shù)1,邏輯讀取11,實(shí)體讀取0,讀取前讀取0,LOB 邏輯讀取0,LOB 實(shí)體讀取0,LOB 讀取前讀取0。
use Northwind
go
--刪除聚集索引
--DROP INDEX [Order Details].INDEX_ProductID
--建立聚集索引
CREATE CLUSTERED INDEX INDEX_ProductID ON [Order Details](ProductID)
go
SET STATISTICS IO ON
select * from [Order Details] where ProductID=42
SET STATISTICS IO OFF
資料表'Order Details'。掃描計(jì)數(shù)1,邏輯讀取2,實(shí)體讀取0,讀取前讀取0,LOB 邏輯讀取0,LOB 實(shí)體讀取0,LOB 讀取前讀取0。
以上可以看出邏輯讀相差很大,由此可以通過(guò)SET STATISTICS IO ON 來(lái)查看邏輯讀,完成同一功能的不同SQL語(yǔ)句,邏輯讀越小查詢速度越快
(8)將鼠標(biāo)移到圖形執(zhí)行計(jì)劃上的表名(以及它的圖標(biāo))上面,就會(huì)彈出一個(gè)窗口,從它上面可以看到一些信息。這些信息讓你知道是否有用到索引來(lái)從表中獲取數(shù)據(jù),以及它是如何使用的。這些信息包括:
(8.1)Table Scan(表掃描):如果看到這個(gè)信息,就說(shuō)明數(shù)據(jù)表上沒(méi)有聚集索引,或者查詢優(yōu)化器沒(méi)有使用索引來(lái)查找。意即資料表的每一行都被檢查到。如果資料表相對(duì)較小的話,表掃描可以非常快速,有時(shí)甚至快過(guò)使用索引。因此,當(dāng)看到有執(zhí)行表掃描時(shí),第一件要做的事就是看看數(shù)據(jù)表有多少數(shù)據(jù)行。如果不是太多的話,那么表掃描可能提供了最好的總體效能。但如果數(shù)據(jù)表大的話,表掃描就極可能需要長(zhǎng)時(shí)間來(lái)完成,查詢效能就大受影響。在這種情況下,就需要仔細(xì)研究,為數(shù)據(jù)表增加一個(gè)適當(dāng)?shù)乃饕糜谶@個(gè)查詢。假設(shè)你發(fā)現(xiàn)某查詢使用了表掃描,有一個(gè)合適的非聚集索引,但它沒(méi)有用到。這意味著什么呢?為什么這個(gè)索引沒(méi)有用到呢?如果需要獲得的數(shù)據(jù)量相對(duì)數(shù)據(jù)表大小來(lái)說(shuō)非常大,或者數(shù)據(jù)選擇性不高(意味著同一個(gè)字段中重復(fù)的值很多),表掃描經(jīng)常會(huì)比索引掃描快。例如,如果一個(gè)數(shù)據(jù)表有10000個(gè)數(shù)據(jù)行,查詢返回1000行,如果這個(gè)表沒(méi)有聚集索引的話,那么表掃描將比使用一個(gè)非聚集索引更快。或者如果數(shù)據(jù)表有10000個(gè)數(shù)據(jù)行,且同一個(gè)字段(WHERE條件句有用到這個(gè)字段)上有1000筆重復(fù)的數(shù)據(jù),表掃描也會(huì)比使用非聚集索引更快。查看圖形執(zhí)行計(jì)劃上的數(shù)據(jù)表上的彈出式窗口時(shí),請(qǐng)注意”預(yù)估的資料行數(shù)(Estimated Row Count)”。這個(gè)數(shù)字是查詢優(yōu)化器作出的多少個(gè)數(shù)據(jù)行會(huì)被返回的最佳推測(cè)。如果執(zhí)行了表掃描且”預(yù)估的數(shù)據(jù)行數(shù)”數(shù)值很高的話,就意味著返回的記錄數(shù)很多,查詢優(yōu)化器認(rèn)為執(zhí)行表掃描比使用可用的非聚集索引更快
(8.2)Index Seek(索引查找):索引查找意味著查詢優(yōu)化器使用了數(shù)據(jù)表上的非聚集索引來(lái)查找數(shù)據(jù)。性能通常會(huì)很快,尤其是當(dāng)只有少數(shù)的數(shù)據(jù)行被返回時(shí)
(8.3)Clustered Index Seek(聚集索引查找):這指查詢優(yōu)化器使用了數(shù)據(jù)表上的聚集索引來(lái)查找數(shù)據(jù),性能很快。實(shí)際上,這是SQL Server能做的最快的索引查找類(lèi)型
(8.4)Clustered Index Scan(聚集索引掃描):聚集索引掃描與表掃描相似,不同的是聚集索引掃描是在一個(gè)建有聚集索引的數(shù)據(jù)表上執(zhí)行的。和一般的表掃描一樣,聚集索引掃描可能表明存在效能問(wèn)題。一般來(lái)說(shuō),有兩種原因會(huì)引此聚集索引掃描的執(zhí)行。第一個(gè)原因,相對(duì)于數(shù)據(jù)表上的整體數(shù)據(jù)行數(shù)目,可能需要獲取太多的數(shù)據(jù)行。查看”預(yù)估的數(shù)據(jù)行數(shù)量(Estimated Row Count)”可以對(duì)此加以驗(yàn)證。第二個(gè)原因,可能是由于WHERE條件句中用到的字段選擇性不高。在任何情況下,與標(biāo)準(zhǔn)的表掃描不同,聚集索引掃描并不會(huì)總是去查找數(shù)據(jù)表中的所有數(shù)據(jù),所以聚集索引掃描一般都會(huì)比標(biāo)準(zhǔn)的表掃描要快。通常來(lái)說(shuō),要將聚集索引掃描改成聚集索引查找,你唯一能做的是重寫(xiě)查詢語(yǔ)句,讓語(yǔ)句限制性更多,從而返回更少的數(shù)據(jù)行
(9)絕大多數(shù)情況下,查詢優(yōu)化器會(huì)對(duì)連接進(jìn)行分析,按最有效率的順序,使用最有效率的連接類(lèi)型來(lái)對(duì)數(shù)據(jù)表進(jìn)行連接。但并不總是如此。在圖形執(zhí)行計(jì)劃中你可以看到代表查詢所使用到的各種不同連接類(lèi)型的圖標(biāo)。此外,每個(gè)連接圖標(biāo)都有兩個(gè)箭頭指向它。指向連接圖標(biāo)的上面的箭頭代表該連接的外部表,下面的箭頭則代表這個(gè)連接的內(nèi)部表。箭頭的另一頭則指向被連接的數(shù)據(jù)表名。有時(shí)在多表連接的查詢中,箭頭的另一頭指向的并不是一個(gè)數(shù)據(jù)表,而是另一個(gè)連接。如果將鼠標(biāo)移到指向外部連接與內(nèi)部連接的箭頭上,就可以看到一個(gè)彈出式窗口,告訴你有多少數(shù)據(jù)行被發(fā)送至這個(gè)連接來(lái)進(jìn)行處理。外部表應(yīng)該總是比內(nèi)部表含有更少的數(shù)據(jù)行。如果不是,則說(shuō)明查詢優(yōu)化器所選擇的連接順序可能不正確
(10) 查看圖形執(zhí)行計(jì)劃時(shí),你可能會(huì)發(fā)現(xiàn)某個(gè)圖標(biāo)的文字用紅色顯示,而非通常情況下的黑色。這意味著相關(guān)的表的一些統(tǒng)計(jì)數(shù)據(jù)遺失,統(tǒng)計(jì)數(shù)據(jù)是查詢優(yōu)化器生成一個(gè)好的執(zhí)行計(jì)劃所必須的, 遺失的統(tǒng)計(jì)數(shù)據(jù)可以通過(guò)右鍵這個(gè)圖標(biāo),并選擇”創(chuàng)建遺失的統(tǒng)計(jì)資料”來(lái)創(chuàng)建。這時(shí)會(huì)彈出”創(chuàng)建遺失的統(tǒng)計(jì)數(shù)據(jù)”對(duì)話框,通過(guò)它可以很容易地創(chuàng)建遺失的統(tǒng)計(jì)數(shù)據(jù)。 當(dāng)可以選擇去更新遺失的統(tǒng)計(jì)資料時(shí),應(yīng)該總是這樣做,因?yàn)檫@樣極有可能讓你正在分析的查詢語(yǔ)句從中獲得效能上的好處
(11) 有時(shí)你會(huì)在圖形執(zhí)行計(jì)劃上看到標(biāo)識(shí)了”Assert”的圖標(biāo)。這意味著查詢優(yōu)化器正在驗(yàn)證查詢語(yǔ)句是否有違反引用完整性或者條件約束。如果沒(méi)有,則沒(méi)有問(wèn)題。但如果有的話,查詢優(yōu)化器將無(wú)法為該查詢建立執(zhí)行計(jì)劃,同時(shí)會(huì)產(chǎn)生一個(gè)錯(cuò)誤
(12) 你常常會(huì)在圖形執(zhí)行計(jì)劃上看到標(biāo)識(shí)成”書(shū)簽查找(Bookmark Lookup)”的圖標(biāo)。書(shū)簽查找相當(dāng)常見(jiàn)。書(shū)簽查找的本質(zhì)是告訴你查詢處理器必須從數(shù)據(jù)表或者聚集索引中來(lái)查找它所需要的數(shù)據(jù)行,而不是從非聚集索引中直接讀取。 打比方說(shuō),如果一個(gè)查詢語(yǔ)句的SELECT,JOIN以及WHERE子句中的所有字段,都不存在于那個(gè)用來(lái)定位符合查詢條件的數(shù)據(jù)行的非聚集索引中,那么查詢優(yōu)化器就不得不做額外的工作在數(shù)據(jù)表或聚集索引中查找那些滿足這個(gè)查詢語(yǔ)句的字段。另一種引起書(shū)簽查找的原因是使用了SELECT *。由于在絕大多情況下它會(huì)返回比你實(shí)際所需更多的數(shù)據(jù),所以應(yīng)該永不使用SELECT *. 從性能方面來(lái)說(shuō),書(shū)簽查找是不理想的。因?yàn)樗鼤?huì)請(qǐng)求額外的I/O開(kāi)銷(xiāo)在字段中查找以返回所需的數(shù)據(jù)行。 如果認(rèn)為書(shū)簽查找防礙了查詢的性能,那么有四種選擇可以用來(lái)避免它:可以建立WHERE子句會(huì)用到的聚集索引,利用索引交集的優(yōu)勢(shì),建立覆蓋的非聚集索引,或者(如果是SQL Server 2000/2005企業(yè)版的話)可以建立索引視圖。如果這些都不可能,或者使用它們中的任何一個(gè)都會(huì)耗用比書(shū)簽查找更多的資源,那么書(shū)簽查找就是最佳的選擇了。
(13) 有時(shí)查詢優(yōu)化器需要在tempdb數(shù)據(jù)庫(kù)中建立臨時(shí)工作表。如果是這樣的話,就意味著圖形執(zhí)行計(jì)劃中有標(biāo)識(shí)成Index Spool, Row Count Spool或者Table Spool的圖標(biāo)。 任何時(shí)候,使用到工作表一般都會(huì)防礙到性能,因?yàn)樾枰~外的I/O開(kāi)銷(xiāo)來(lái)維護(hù)這個(gè)工作表。理想情況下應(yīng)該不要用到工作表。不幸的是并不能總是避免用到工作表。有時(shí)當(dāng)使用工作表比其它選擇更有效率時(shí),它的使用實(shí)際上會(huì)增強(qiáng)性能。不論何種情況,圖形執(zhí)行計(jì)劃中的工作表都應(yīng)該引起你的警覺(jué)。應(yīng)該仔細(xì)檢查這樣的查詢語(yǔ)句,看看是否有辦法重寫(xiě)查詢來(lái)避免用到工作表。有可能沒(méi)有辦法。但如果有的話,你就朝提升這個(gè)查詢的性能方面前進(jìn)了一步.
(14) 在圖形執(zhí)行計(jì)劃上看到流聚合(Stream Aggregate)圖標(biāo)就意味著有對(duì)一個(gè)單一的輸入進(jìn)行了聚合。當(dāng)使用了DISTINCT子句,或者任何聚合函數(shù)時(shí),如AVG, COUNT, MAX, MIN,或者SUM等,流聚合操作就相當(dāng)常見(jiàn)。?
(15)查詢分析器與Management Studio不是唯一的可以生成、顯示查詢執(zhí)行計(jì)劃的工具。SQL Server Profiler也可以顯示執(zhí)行計(jì)劃,但格式是文本形式的。使用SQL Server Profiler來(lái)顯示執(zhí)行計(jì)劃的一個(gè)優(yōu)勢(shì)是,它能為實(shí)際運(yùn)行的大量查詢產(chǎn)生執(zhí)行計(jì)劃。如果使用查詢分析器和Management Studio,則一次只能運(yùn)行一個(gè)
(16)如果在查詢中使用了OPTION FAST提示,那就必須小心執(zhí)行計(jì)劃的結(jié)果可能不是你所期望的。這時(shí)你所看到的執(zhí)行計(jì)劃基于使用了FAST提示的結(jié)果,而不是整個(gè)查詢語(yǔ)句的實(shí)際執(zhí)行計(jì)劃。FAST提示用來(lái)告知果詢優(yōu)化器盡可能快地返回指定行數(shù)的數(shù)據(jù)行,即便這樣做會(huì)防礙查詢的整體性能。使用這個(gè)提示的目的在于為使用者快速返回特定行數(shù)的記錄,由此讓他們產(chǎn)生速度非常快速的錯(cuò)覺(jué)。。當(dāng)返回指定行數(shù)的數(shù)據(jù)行后,剩余的數(shù)據(jù)行按照它們通常的速度返回。 因此,如果使用了FAST提示,那么生成的執(zhí)行計(jì)劃只是基于那些FAST返回的數(shù)據(jù)行,而非查詢要返回的所有數(shù)據(jù)行。如果想看所有數(shù)據(jù)行的執(zhí)行計(jì)劃,那么就必須移除這個(gè)FAST提示.
轉(zhuǎn)載于:https://www.cnblogs.com/kelamayi/archive/2009/12/08/1619347.html
總結(jié)
以上是生活随笔為你收集整理的剖析SQL Server执行计划的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ERP 我最看重什么?
- 下一篇: SQL2005 用户自定义类型