SQL Server中掃描(scan)和查詢(seek)這兩種演算法的區別
SQL SERVER使用掃描(scan)和查詢(seek)這兩種演算法從資料表和索引中讀取資料。這兩種演算法構成了查詢的基礎,幾乎無處不在。Scan會掃描並且返回整個表或整個索引。 而seek則更有效率,根據謂詞(predicate),只返索引內的一個或多個範圍內的資料。下面將以如下的查詢語句作為例子來分析scan和seek:
select OrderDate from Orders where OrderKey = 2
Scan
使用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 |