非聚集索引中的临界点(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月 索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度. 索引包含由表或视图中的一列或多列生成的键. ...
随机推荐
- jquery实现返回基部案例效果
<!doctype html> <html> <head> <meta charset="gb2312"> <title> ...
- 【LeetCode】Power of Two
问题描写叙述 Given an integer, write a function to determine if it is a power of two. 意:推断一个数是否是2的n次幂 算法思想 ...
- Linux2.6 内核的 Initrd 机制解析(转)
from: https://www.ibm.com/developerworks/cn/linux/l-k26initrd/ 简介: Linux 的 initrd 技术是一个非常普遍使用的机制,lin ...
- Codeforces Round #381 (Div. 1) A. Alyona and mex 构造
A. Alyona and mex 题目连接: http://codeforces.com/contest/739/problem/A Description Alyona's mother want ...
- Android定位&地图&导航——基于百度地图移动获取位置和自动定位
一.问题描述 使用百度地图实现如图所示应用,首先自动定位当前我起始位置(小圆点位置),并跟随移动不断自动定位我的当前位置 百度Api不同版本使用会有些差异,本例中加入lib如下: 二.编写MyAppl ...
- Android Studio 快捷键 for mac
Action Mac OS Win/Linux 打开文件 Cmd + shift + O 打开Class文件 Cmd + O 覆写方法 Ctrl + O 生成方法(重写构造.setter ...
- [Scheme]一个Scheme的Metacircular evaluator
这个解释器可以用来跑前面两篇文章的例子,所以一并扔出来,三部曲哈哈. Lisp内置的S-expression相当于解析好的语法树,而借助quasiquote和unquote又很容易进行语法树层面的变换 ...
- OpenCV 2.4.8组件结构全解析
转自: http://blog.csdn.net/huang9012/article/details/21811271 之前啃了不少OpenCV的官方文档,发现如果了解了一些OpenCV整体的模块架构 ...
- session和jsessionid有什么关系
首先,并不是说你一打开一个页面就会产生一个session. 所谓session你可以这样理解:当你与服务端进行会话时,比如说登陆成功后,服务端会为你开壁一块内存区间,用以存放你这次会话的一些内容,比如 ...
- (笔记)Linux内核学习(七)之内核同步机制和实现方式
一 原子操作 指令以原子的方式执行——执行过程不被打断. 1 原子整数操作 原子操作函数接收的操作数类型——atomic_t //定义 atomic_t v;//初始化 atomic_t u = AT ...