非聚集索引中的临界点(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月 索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度. 索引包含由表或视图中的一列或多列生成的键. ...
随机推荐
- Python中使用自定义类class作为海量数据结构时内存占用巨大的问题
最近碰到处理一个二十多兆的文件时内存蹭蹭的吃掉四百多兆,吓死宝宝了. 无奈毕竟接触python时间有限,还没有完整的看过python的一些基础知识,我想一个合格的pythoner应该不会碰到这个问题. ...
- Linux之重定向
Linux重定向是指修改原来默认的一些东西,对原来系统命令的默认执行方式进行改变,比如说简单的我不想看到在显示器的输出而是希望输出到某一文件中就可以通过Linux重定向来进行这项工作. Linux默认 ...
- SDN论坛看到BW的问题及相关解答
SDN论坛看到BW的问题及相关解答 链接: http://blog.sina.com.cn/s/blog_5c58e3c70100r1ou.html 现在有一个 QUERY 运行十分慢 , 所以我想在 ...
- Asp.net Core WebApi 全局异常类
通过全局异常类,所有程序中遇到的错误都会被拦截,并友好的返回结果. 1.自定义一个全局异常处理类中间件 using Microsoft.AspNetCore.Http; using Newtonsof ...
- 阿里云里面的Linux 系统挂载数据盘
转自:http://www.cnblogs.com/adjk/p/5112360.html 适用系统:非IO优化+SSD云盘Linux(Redhat , CentOS,Debian,Ubuntu)实例 ...
- sudo 使用不了, the permissions on the /etc/sudoers file are changed to something other than 0440
sudo 使用不了,报错: the permissions on the /etc/sudoers file are changed to something other than 0440 how ...
- wav 转换到 flac
参考自:http://so.trust.blog.163.com/blog/static/17188620020127197618621/ wav 无损无压缩: flac无损压缩 将 wav 转换到 ...
- Java Web dev搭建经验总结
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! 回馈分析法使我看到,我对专业技术人员,不管是工程师.会计师还是市场研究人员,都容易从直觉上去理解 ...
- Linux网络流量实时监控ifstat iftop命令详解
ifstat 介绍 ifstat工具是个网络接口监测工具,比较简单看网络流量 实例 默认使用 #ifstat eth0 eth1 KB /s i ...
- WebApp MVC 框架的开发细节归纳
在前文<WebApp MVC,“不一样”的轻量级互联网应用程序开发框架>介绍了WebApp MVC的技术实现以及如何使用,而在本章进一步归纳了使用框架开发的一些细节,也给我们在开发具体功能 ...