想複雜的事情簡單說,在看執行計劃的其他文章的時候,發現直接上很複雜的DDL腳本來講解,這樣子可能打開就沒有興趣往下看了。所以這裏用了一個最簡單的select語句進行說明引新入門。
打開
注意這三個【L型圖標】,可以把鼠標移動到按鈕上方可以顯示【解釋文字】。
圖中從左到右分別爲【顯示估計的執行計劃】【包括實際的執行計劃】【包括實時查詢統計信息】。
【顯示估計的執行計劃】是執行某個DDL的估計值。
【包括實際的執行計劃】【包括實時查詢統計信息】都是執行實際的值,所以你選擇後,它會在下次執行後出執行結果。
結果分析
這是一個簡單的查詢,鼠標點擊圖片中的圖片或線上都有驚喜(大量細節信息展示)。執行計劃可以通過“另存”操作將某一次結果保留下來,方便與日後進行結果對比。
除了圖標外,線的粗細代表涉及到的數據量的大小,越粗代表數據量越大。
執行計劃元素列表如下:
Select (Result) | Sort | Spool |
Clustered Index Scan | Key Lookup | Eager Spool |
NonClustered Index Scan | Compute Scalar | Stream Aggregate |
Clustered Index Seek | Constant Scan | Distribute Streams |
NonClustered Index Seek | Table Scan | Repartition Streams |
Hash Match | RID Lookup | Gather Streams |
Nested Loops | Filter | Bitmap |
Merge Join | Lazy Spool | Split |
執行計劃元素列表解釋請參考:
https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms175913(v=sql.105)
結果細節圖
其中,第三個圖中的屬性Ordered是與order by相關。如下面兩個腳本:
select * from vvtest order by id;
select * from vvtest
兩個執行計劃的結果如下:
所謂數據訪問就是直接訪問數據,可以是訪問一個表也可以是訪問一個索引。
通常有兩種方法:一種是掃描(scan)一種是查找(seek)。
- scan就是讀取整個結構,可以訪問一個heap或者一個clustered索引或者一個non-clustered索引。
- seek不會讀取整個結構,他則是更高效地通過索引訪問一行,所以從這個角度來看,查找就只能應用在索引上面了。簡單總結如下表所示:
首先分析最右邊的Table Scan。這是sqlserver 查詢數據的方法。sqlserver 數據查詢方式一共有五種:
1. 【Table Scan】:遍歷整個表,查找所有匹配的記錄行。這個操作將會一行一行的檢查,當然,效率也是最差的。
2. 【Index Scan】:根據索引,從表中過濾出來一部分記錄,再查找所有匹配的記錄行,顯然比第一種方式的查找範圍要小,因此比【Table Scan】要快。
3. 【Index Seek】:根據索引,定位(獲取)記錄的存放位置,然後取得記錄,因此,比起前二種方式會更快。
4. 【Clustered Index Scan】:和【Table Scan】一樣。注意:不要以爲這裏有個Index,就認爲不一樣了。 其實它的意思是說:按聚集索引來逐行掃描每一行記錄,因爲記錄就是按聚集索引來順序存放的。 而【Table Scan】只是說:要掃描的表沒有聚集索引而已,因此這二個操作本質上也是一樣的。
5. 【Clustered Index Seek】:直接根據聚集索引獲取記錄,最快!
所以總體來講,在查詢結果集是相同數量的情況下,查詢速度排序爲5>3>2>4>1。
一般而言,在性化時可以看到執行記錄時是不是【Table Scan】或者【Clustered Index Scan】,如果是,可以通過增加或修改索引類型進行效率上的對比。
Lookup類型
我是真的服氣這麼多名詞解釋。
Bookmark Lookup、RID Lookup、Key Lookup。
Bookmark Lookup和Key Lookup是一個意思,等價。
如果表沒有創建聚集索引則稱爲Bookmark Lookup,如果表中沒有聚集索引但是存在非聚集索引我們稱爲RID Lookup。
爲什麼突然扯了一嘴Lookup呢?因爲Lookup其實就是與執行計劃裏的scan或index相關。如果聚集索引命中就是指Bookmark Lookup——聚集索引命中的時候,很大概率是索引不能帶出select所需的某一部分字段或者是全部字段,所以需要先命中一行,然後把某一行的數據全帶出來。非聚集索引命中就是指RID Lookup。
SQL SERVER如何選擇執行計劃
sqlserver選擇某個執行計劃,執行計劃用某個索引,是這有一個權重判斷的。這個權重在執行前都計算出來了。那怎麼查看呢——通過【索引統計信息】。在執行腳本的過程中,sqlserver會根據這些統計信息,選擇一個它認爲是最合適的方法去執行查詢過程。統計信息可以自動定時更新,在SQL Server中也有個參數來控制這個更新方式。
語法
DBCC SHOW_STATISTICS ("Person.Address", AK_Address_rowguid);
這裏的引號很重要,沒會報錯。
主要有兩個參數:
第一個:是表名或者是索引視圖名(給視圖加的索引)
第二個:索引名、列名、統計信息。
結果屬性介紹
索引前綴集是什麼?索引前綴和前綴索引不是一個東西。
索引前綴是指索引的選擇性——根據索引定義時的字段順序來決定索引是否被命中。如果是索引(a,b,c),命中查詢的時候是查詢條件中有(a) (a,b) (a,b,c)進行命中,但是(b,c)這個查詢條件不會命中該索引。所以索引前綴也叫索引列前綴集。
前綴索引說白了就是對文本的前幾個字符(具體是幾個字符在建立索引時指定)建立索引,這樣建立起來的索引更小,所以查詢更快。
屬性 | 介紹 | 備註 |
---|---|---|
表1 | 列出了這個索引統計信息的主要信息。 | -- |
Name | 統計信息的名稱 | 語法裏的第二個參數 |
Updated | 上一次更新統計信息的日期和時間。 | |
Rows | 表中的行數。 | |
Rows Sampled | 統計信息的抽樣行數。 | |
Step |
數據可分成多少個組,與第三個表有多少行相對應。 |
|
Desity | 中文翻譯:密度。根據索引列計算不同值的分佈密度。 | Calculated as 1 / distinct values for all values in the first key column of the statistics object |
Average key Length | 所有索引列的平均長度。 | |
String Index | 如果爲“YES”,則統計信息中包含字符串摘要索引,以支持爲 LIKE 條件估算結果集大小。僅適用於 char、varchar、nchar 和 nvarchar、varchar(max)、nvarchar(max)、text 以及 ntext 數據類型的前導列 | |
Filter Expression | 謂詞表達式 | |
Unfiltered Rows |
上面的Filter Expression所操作的數據總行數 爲NULL,代表Filter Expression沒有匹配到具體數據。 |
|
表2 | 它列出各種字段組合的選擇性,數據越小表示重複越性越小,當然選擇性也就越高。 | -- |
All density | 索引列前綴集的選擇性(包括 EQ_ROWS)。注意:這個值越小就表示選擇性越高。 | 如果這個值小於0.1,這個索引的選擇性就比較高,反之,則表示選擇性就不高了。 |
Average Length | 索引列前綴集的平均長度。 | |
Columns | 爲其顯示 All density 和 Average length 的索引列前綴的名稱 | |
表3 | 數據分佈的直方圖,SQL Server就是靠它預估一些執行步驟的數據量。 | -- |
RANGE_HI_KEY | 直方圖的數據最大值 | |
RANGE_ROWS | 每組數據組的估算行數,不包含最大值。 | |
EQ_ROWS | 每組數據組中與最大值數據(RANGE_HI_KEY)相等的行數目 | 估計值 |
DISTINCT_RANGE_ROWS | 每組數據組中的非重複值的估算數目,不包含最大值。 | |
AVG_RANGE_ROWS | 每組數據組中的重複值的平均數目,不包含最大值,計算公式:RANGE_ROWS / DISTINCT_RANGE_ROWS for DISTINCT_RANGE_ROWS > 0 | |
執行計劃詳細屬性介紹
在執行DDL之前,輸入一句set statistics profile on 。顯示一個表格,表格內是比圖上更細節的實際的執行信息。針對這個表的屬性做一個整理解析。
屬性 | 字段介紹 | 備註 |
---|---|---|
Rows(重要屬性) | 在某個步驟中,實時產生的記錄條數 | 真實數據 |
Executes(重要屬性) | 某個步驟被執行的次數。 | 真實數據 |
StmtTest(重要屬性) |
執行步驟的描述 1) |--Table Scan(OBJECT:([master].[dbo].[vvtest])) 2) |--Clustered Index Scan(OBJECT:([master].[dbo].[vvtest].[ClusteredIndex-20190531-153224])) 3)select * from vvtest |
1和2是加了沒有聚集索引和把id作聚集索引後的對比。3是DDL是執行計劃最外層的腳本執行。2中的加黑部分是在查詢方式是Clustered Index Scan的時候他使用到的索引名稱。 所以這個屬性可以看到使用的索引和實際步驟裏執行的DDL腳本。 |
StmtId |
當前執行語句的單條DDL的編號。 |
set statistics profile on select * from vvtest order by id;——這條就是1 select * from vvtest——這條就是2 |
NodeId | 單條DDL裏的步驟編號。 | 從1開始,單個執行計劃中箭頭最右邊是1,最左邊(被指的)依次增加,代表單個DDL內的執行順序。 |
Parent | 與該條步驟相關的下一步步驟編號(NodeId) | |
PhysicalOp | sqlserver 數據查詢方式,包括但是不限於我們上面提到的五種Table Scan…… | 只有節點類型Type=PLAN_ROWS 的纔有這個屬性。Type屬性在下方介紹 |
LogicalOp | 關係運算符 | 只有節點類型Type=PLAN_ROWS 的纔有這個屬性。Type屬性在下方介紹 |
Argument | 被PhysicalOp執行的對象,細節信息,比如說當前被使用到的列名或表名 |
舉例: Sort——ORDER BY:([master].[dbo].[vvtest].[id] ASC) Table Scan ——OBJECT:([master].[dbo].[vvtest]) |
DefinedValues | 將DDL語句進行細化成一個用逗號分隔的列表。 | |
EstimateRows | 估計返回多少行數據 | 估計值 |
EstimateIO | 估計IO開銷,單位是 | 估計值 |
EstimateCPU | 估計CPU開銷,計算的是CP佔用率(百分比) | 估計值 |
AvgRowSize | 估計本步驟的數據每行平均的字節數,單位是bytes | 估計值 |
TotalSubtreeCost | 估計的本步驟和本步驟涉及到所有子步驟的總消耗的佔總的百分比 | 估計值 |
OutPutList | 這個屬性是一個用逗號分隔的列表。列表裏是本步驟執行的DDL,輸出涉及到對應表的列名。 | 列名集合 |
Warnings | 用逗號分隔的列表,本步驟DDL會觸發的警告信息。 | |
Type | 節點類型 |
1)如果是陳述性SQL,那麼Type的值爲SELECT, INSERT, EXECUTE等。 2)如果是執行計劃的子節點(如索引執行的描述),那麼Type的值爲PLAN_ROW |
Parallel |
並行標誌。 0-本步驟不能並行執行 1-本步驟可以並行執行 |
|
EstimateExecutions | 估計本步驟在總的執行中被執行的次數。 |
估計值。 有可能在腳本中步驟被執行N次 |
以上屬性的英文版的解釋在:
參考文檔:https://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html