SQL Server中SCAN 和SEEK的区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
参考技术A   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

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,可以每次只重新编译存储过程的一部分(当然,你也可以选择重新编译整个存储过程,这取决于你的需求。详见联机文档。)

以上是关于SQL Server中SCAN 和SEEK的区别的主要内容,如果未能解决你的问题,请参考以下文章

SQL SERVER查看索引使用情况

index seek和index scan 提高sql 效率

Clustered Index Scan 与 Clustered Index Seek

SQL Server里的闩锁耦合(Latch Coupling)

index seek与index scan

SQL Server中LIKE %search_string% 走索引查找(Index Seek)浅析