非聚集索引中的临界点(Tipping Point)
什么是临界点?
注意,我要说的问题是非聚集索引的执行计划从Seek+Lookup变成Table/Clustered Index Scan的临界点。SQL Server的访问数据的IO最小单元是页。
我们知道聚集索引的叶级是数据页,非聚集索引的叶级是指向数据行的指针。所以通过聚集索引获取数据时,就是直接访问聚集索引本身,而通过非聚集索引获取数据时,除了访问自身,还要通过指针去访问数据页。这个过程就是RID/Key Lookup。而此Lookup是一个单页操作,即每次使用一个RID/Key,然后去访问对应的一个数据页,然后获取页上的相应的数据行。可能当前数据页的有多个数据行是符合查询要求的,但是一次lookup,只能取当前的RID/Key指定的数据行。所以同一个数据页,可能要被访问很多次。例如,现在lookup要去找RID为2,3,5,7,9对应的数据行,而这5个数据行都存在数据页N上,则数据页N只少要被访问5次。
在Seek时,如果要返回N行数据,则Seek操作至少要访问N次数据页。当Lookup访问次数据超过了全表数据页的总数时,就会出现临界点。这个时候Scan操作成本要比Lookup低。超过这个临界点时,查询优化器一般会选择Scan替代Seek+Lookup。例如表T有100000行,每页存放100行,共有1000页。查询1000条数据,理论/理想情况下:Scan最少时只需要10次IO,Lookup至少需要1000次IO。
需要注意的是覆盖索引中不存在RID/Key,而是对应的列值,所以不会出现这个问题。
临界点什么时候出现?
前面说的理论和原理上的东西,而实际临界点的出现由很多因素决定。但主要与表的总页数相关。临界点大概出现在访问页占全表页数的25%~33%时。为了直观,通常把页数再转换成行数来分析。转换时需要注意,前面阐明Lookup是单页操作,所以页数=行数。
- 一个表总行数为1,000,000,每页存放2条行数,共500,000页。则25%=125,000,33%=166,000。临界点会出现在125,000页和166,000页间。转换成行表示就是125000/1,000,000=12.5%,166000/1,000,000=16.6%。也就是说当返回行数小于125000时,很可能会使用Lookup。当返回行数大于166000时,很可能会使用Scan。这个表的行太宽了,一个页只能存放2行数据,从百分比看起来没有什么太大感觉。
- 一个表总行数为1,000,000,每页存放100条行数,共10,000页。则25%=2500,33%=3300。转换成行2500/1000000=0.25%,3300/1000000=0.33%。它的临界点上限不到全表行数的0.5%。也就是说你查询表中不到0.5%的行数时就会全表扫描。
- 一个表总行数为1,000,000,每页存放20条行数,共50,000页。则25%=125,00,33%=166,00。转换成行表示就是12500/1000000=1.25%,16600/1000000=1.66%。
不难发现,临界点判断,对于大表的查询性能是有很大帮助的。而对于小表而言,几乎都会是Scan,但是数据库有缓存机制,小表会完整缓存,扫描影响也不大。
我们能做些什么?
1. 很容易想到,既然表有Seek对应的索引,我们使用Hint强制使用Seek,问题不就解决了。这个不一定,本来这个问题的出现就是查询优化器认为Scan比Lookup的成本要低。如果你强制可能会适得其反。SQL Server的查询优化器是很强大和智能的,除非你严格测试过,证明ForceSeek性能更好一些。
2. 如果条件允许,建立一个针对查询的覆盖查询,借此消除Lookup操作。
示例分析
使用AdventureWorks2012的Sales.SalesOrderDetail。在ProductID列有一个非聚集索引IX_SalesOrderDetail_ProductID。
通过下的查询可以知道表有121317行,共1237个数据页,每页大约存放98行数据。由此我们可以预估一下临界点在(309行,408行)附近。
select page_count,record_count
from sys.dm_db_index_physical_stats(db_id(),object_id(N'Sales.SalesOrderDetail'),1,null,'detailed')
where index_level=0
然后再统计一下不同的ProductID在表中行数,好针对性的测试不同ProductID:
select ProductID,COUNT(*) as cnt
from Sales.SalesOrderDetail
group by ProductID
order by cnt
通过上面查询,我们知道ProductID=882在表中有407行,可以看到它还是使用Lookup的方式。它的IO计数为:
Table 'SalesOrderDetail'. Scan count 1, logical reads 1258

ProductID=751在表中有409行,它就使用了Scan的方式。它的IO计数为:
Table 'SalesOrderDetail'. Scan count 1, logical reads 1246

我们还可以测试返回行数更多的ProductID,如果是扫描的方式则IO都是在1246,如果是Lookup则都会高于1246。证明跟理论还是契合的。
就算500行返回才会超过临界点,而500行也只占总行数的500/121317=0.41%。也就是说当返回行数超过全表的0.41%时,优化器就认为它的筛选度不够高了,不用seek+lookup,要扫描了。
总结
1. 当遇到"明明有索引,为什么会扫描?",临界点的问题可能是原因之一。
2. 因为存在临界点,所以非覆盖非聚集索引的使用率可能没有我们想象的高。
参考
http://www.sqlskills.com/blogs/kimberly/the-tipping-point-query-answers/
非聚集索引中的临界点(Tipping Point)的更多相关文章
- SQL Server的非聚集索引中会存储NULL吗?
原文:SQL Server的非聚集索引中会存储NULL吗? SQL Server的非聚集索引中会存储NULL吗? 这是个很有意思的问题,下面通过如下的代码,来说明,到底会不会存储NULL. --1.建 ...
- SQL Server中的聚集索引(clustered index) 和 非聚集索引 (non-clustered index)
本文转载自 http://blog.csdn.net/ak913/article/details/8026743 面试时经常问到的问题: 1. 什么是聚合索引(clustered index) / ...
- SQL Server 索引和表体系结构(非聚集索引)
非聚集索引 概述 对于非聚集索引,涉及的信息要比聚集索引更多一些,由于整个篇幅比较大涉及接下来的要写的“包含列的索引”,“索引碎片”等一些知识点,可能要结合起来阅读理解起来要更容易一些.非聚集索引和聚 ...
- SQLSERVER聚集索引与非聚集索引的再次研究(下)
SQLSERVER聚集索引与非聚集索引的再次研究(下) 上篇主要说了聚集索引和简单介绍了一下非聚集索引,相信大家一定对聚集索引和非聚集索引开始有一点了解了. 这篇文章只是作为参考,里面的观点不一定正确 ...
- SQL SERVER 索引之聚集索引和非聚集索引的描述
索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度. 索引包含由表或视图中的一列或多列生成的键. 这些键存储在一个结构(B 树)中,使 SQL Server 可以快速有效地查找与键值关 ...
- SQL Server索引 (原理、存储)聚集索引、非聚集索引、堆 <第一篇>
一.存储结构 在SQL Server中,有许多不同的可用排列规则选项. 二进制:按字符的数字表示形式排序(ASCII码中,用数字32表示空格,用68表示字母"D").因为所有内容都 ...
- SQL Server 非聚集索引的覆盖,连接,交叉和过滤 <第二篇>
在SQL Server中,非聚集索引其实可以看做是一个含有聚集索引的表,但相对实际的表来说,非聚集索引中所存储的表的列数要少得多,一般就是索引列,聚集键(或RID).非聚集索引仅仅包含源表中的非聚集索 ...
- SQL Server索引 - 聚集索引、非聚集索引、非聚集唯一索引 <第八篇>
聚集索引.非聚集索引.非聚集唯一索引 我们都知道建立适当的索引能够提高查询速度,优化查询.先说明一下,无论是聚集索引还是非聚集索引都是B树结构. 聚集索引默认与主键相匹配,在设置主键时,SQL Ser ...
- 聚集索引VS非聚集索引
聚集索引VS非聚集索引 SQL Server 2014 发布日期: 2016年12月 索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度. 索引包含由表或视图中的一列或多列生成的键. ...
随机推荐
- ie8下jquery改变PNG的opacity出现黑边,ie6下png透明解决办法
目前互联网对于网页效果要求越来越高,不可避免的用到PNG图片,PNG分为几种格 式,PNG8 PNG24 PNG32,其中最常用的,也是显示效果和大小比较适中的则是PNG24,支持半透明,透明,颜色也 ...
- Meaningful Use 中与HL7相关的消息及医疗文档
汇总 HL7 消息 入出转消息 ADT A01,A03,A04,A08 免疫消息 VXU V04 电子处方消息 NEWRX Message v8.1 EDIFACT, v8.1 XML, v10.6 ...
- Logstash——multiline 插件,匹配多行日志
本文内容 测试数据 字段属性 按多行解析运行时日志 把多行日志解析到字段 参考资料 在处理日志时,除了访问日志外,还要处理运行时日志,该日志大都用程序写的,比如 log4j.运行时日志跟访问日志最大的 ...
- Form 表单中的Input元素回车时不保存表单
在Form表单中如果直接在Input元素里敲回车键,那么默认将提交表单,可以通过keydown事件取消默认此操作 $("form").live('keydown',function ...
- RS开发中的一些小技巧[不定期更新]
从9月份一直忙到了现在,项目整体的改版工作也完成了十有八九了,有些事情只有你自己真正的做了,你才能明白:哦,原来还可以这个样子,这样做真的好了很多呢,接下来我就分享一些最近遇到的RS开发的一些小技巧, ...
- vs2010 使用vs online账号 需要安装的插件
VS10SP1-KB2662296.exe http://pan.baidu.com/s/1qWDpEG0 vs2010 需要先升级到SP1
- python在windows平台的pip安装package时的编译问题
在安装pcapy时,出现以下错误: building 'pcapy' extension error: Microsoft Visual C++ 9.0 is required (Unable to ...
- System.Diagnostics.Debug和System.Diagnostics.Trace 【转】
在 .net 类库中有一个 system.diagnostics 命名空间,该命名空间提供了一些与系统进程.事件日志.和性能计数器进行交互的类库.当中包括了两个对开发人员而言十分有用的类——debug ...
- WebRTC is for Losers:WebRTC是输家
该文章是引述,仅代表作者Dave Michels观点 WebRTC is for Losers WebRTC technology has fallen short on many of its pr ...
- spring 集成 Hibernate4.3.X org.hibernate.service.jta.platform.spi.JtaPlatform异常
使用Spring3.2.4集成Hibernate4.3.5时,出现以下异常 Causedby:java.lang.ClassNotFoundException:org.hibernate.servic ...