SQL Server中SCAN 和SEEK的区别
SQL Server中SCAN 和SEEK的區別
SQL SERVER使用掃描(scan)和查找(seek)這兩種算法從數據表和索引中讀取數據。這兩種算法構成了查詢的基礎,幾乎無處不在。Scan會掃描并且返回整個表或整個索引。 而seek則更有效率,根據謂詞(predicate),只返索引內的一個或多個范圍內的數據。下面將以如下的查詢語句作為例子來分析scan和seek:
select OrderDate from Orders where OrderKey = 2Scan
使用Scan的方式,SQL Server 會去讀取Orders表中的每一行數據,讀取的時候評估是否滿足謂詞 “where order=2”。如果滿足(數據行符合條件),則返回該行。這個例子里,我們將這個謂詞稱作“residual predicate”。為了得到最優的性能,SQL會盡可能地在掃描中使用“residual predicate”。但如果residual predicate的開銷過于昂貴,SQL Server可能會使用單獨的“filter iterator”. “residual predicate”以where關鍵字的形式出現在文本格式的plan中。對XML格式的plan,則是<predicate>標記的形式。
下面這個掃描的文本格式的plan的結果:
? |–Table Scan(OBJECT:([ORDERS]), WHERE:([ORDERKEY]=(2)))
下圖說明了掃描的方式:
無論數據行是否滿足條件,掃描的讀取方式都會訪問表中的每一個數據,所以scan的成本和表的數據總量是成比例的。 因此,如果表很小或者表內的大多數數據多滿足謂詞,scan是一種有效率的讀取方式。然而如果表很大或者絕大多數的數據并不滿足謂詞, 那么這種方式會讓我們訪問到太多不需要的數據頁面,并執行更多的額外的IO操作。
Seek
繼續以上面的查詢為例子,如果在orderkey列上有一個索引,那么seek可能會是一個好的選擇。使用seek的訪問方式,SQL Server會使用索引直接導向到滿足謂詞條件的數據行。 這個例子里,我們將這個謂詞稱為“seek predicate”。 大多數情況下,SQL Server不必將“seek predicate”重新評估為“residual predicate”。 索引會保證“seek”只返回符合條件的數據行。“seek predicate”以seek關鍵字的形式出現在文本格式的plan中。 對于xml 格式的plan,則以<seekpredicates>標記出現。
下面是使用seek的文本格式的plan的結果:
|–Index Seek(OBJECT:([ORDERS].[OKEY_IDX]), SEEK:([ORDERKEY]=(2)) ORDERED FORWARD)
使用seek時,SQL Server只會直接訪問到滿足條件的數據行和數據頁,因此它的成本只跟滿足條件的數據行的及其相應的數據頁面數量成比例,?和基表的數據量完全沒有關系。因此,如果對于一個選擇性很高(通過這個謂詞,可以篩選掉表中的大部分數據)的謂詞條件,seek是非常高效的。
下面的表格列出了seek和scan這兩種查找方式和堆表,聚簇索引和非聚簇索引的各種組合:
| ? ? ? ? | Scan | Seek |
| Heap | Table Scan | ? |
| Clustered Index | Clustered Index Scan | Clustered Index Seek |
| Non-Clustered Index | Index Scan | Index Seek |
?
?
index seek與index scan
低效 Index Scan(索引掃描):就全掃描索引(包括根頁,中間頁和葉級頁):?
高效
Index Seek(索引查找):通過索引向前和向后搜索 :
?
?解釋解釋index?seek和index?scan:?索引是一顆B樹, index?seek是查找從B樹的根節點開始,一級一級找到目標行。 index?scan則是從左到右,把整個B樹遍歷一遍。 假設唯一的目標行位于索引樹最右的葉節點上(假設是非聚集索引,樹深度2,葉節點占用k頁物理存儲)。 index?seek引起的IO是4,而index?scan引起的IO是K,性能差別巨大。
seek:從B樹根到葉節點的過程 掃描:當SEEK完成后,在葉節點執行范圍或全部掃描(按查詢的選擇性會有不同
關于索引,可以仔細讀讀聯機文檔關于物理數據庫體系結構部分 ???? 查詢條件中不要包含運算
這些運算包括字符串連接(如:select * from Users where UserName + ‘pig’ = ‘張三pig’),通配符在前面的Like運算(如:select * from tb1 where col4 like ‘�’),使用其他用戶自定義函數、系統內置函數、標量函數等等(如:select * from UserLog where datepart(dd, LogTime) = 3)。
???????? SQLServer在處理以上語句時,一樣沒辦法估算開銷。最終結果當然是clustered index?scan或者table?scan了。
??? 查詢條件中不要包含同一張表內不同列之間的運算所謂的“運算”包括加減乘除或通過一些function(如:select * from tb where col1 – col2 = 1997),也包括比較運算(如:select * from tb where col1 > col2)。這種情況下,SQLServer一樣沒辦法估算開銷。不論col1、col2上都有索引還是創建了col1、col2上的覆蓋索引還是創建了col1 include col2的索引。
但是這種查詢有解決辦法,可以在表上多創建一個計算字段,其值設置為你的“運算”結果,再在該字段上創建一個索引,就Ok了。
(結果集/總行數)被稱為選擇性,比值越大,選擇性就越高。
你得到了它,本文的重點就是選擇性。
統計信息,說白了,就是表中某個字段取某個值時有多少行結果集。統計信息可以說是一種選擇性的度量,SQLServer就是根據它來估算不同查詢計劃的優劣。若表中總行數為1w,采樣行數為1w。provider_no值為21的只有1行,而值為500的行則有4824行。
?
我們知道,SQLServer會緩存查詢計劃,假如有這么一個存儲過程:
create proc myproc
(
??? @pno int
)
as
select * from charge where provider_no = @pno
第一次我們傳進來一個21,OK,它會緩存該存儲過程的執行計劃為nonclustered index?seek那個。后來我們又傳進來一個500,完蛋了,服務器發現它有一個myproc的緩存,so,又通過nonclustered index?seek執行,接著你的同伙看到你的查詢花費了巨量的IO,于是,你被鄙視了。
這說明了啥?說明如果你的查詢選擇性變動劇烈,你應該告訴SQLServer不要緩存查詢計劃,每次都應該重新評估、編譯。實現方法很簡單,查詢的尾巴上加一個option(recompile)好了。而且SQL2k5還有一個nb的 feature,可以每次只重新編譯存儲過程的一部分(當然,你也可以選擇重新編譯整個存儲過程,這取決于你的需求。詳見聯機文檔。)
?
轉載于:https://www.cnblogs.com/gered/p/8780177.html
總結
以上是生活随笔為你收集整理的SQL Server中SCAN 和SEEK的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么做梦老是梦到以前的老房子
- 下一篇: mustache 模板使用