什么是临界点?

注意,我要说的问题是非聚集索引的执行计划从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)的更多相关文章

  1. SQL Server的非聚集索引中会存储NULL吗?

    原文:SQL Server的非聚集索引中会存储NULL吗? SQL Server的非聚集索引中会存储NULL吗? 这是个很有意思的问题,下面通过如下的代码,来说明,到底会不会存储NULL. --1.建 ...

  2. SQL Server中的聚集索引(clustered index) 和 非聚集索引 (non-clustered index)

    本文转载自  http://blog.csdn.net/ak913/article/details/8026743 面试时经常问到的问题: 1. 什么是聚合索引(clustered index) / ...

  3. SQL Server 索引和表体系结构(非聚集索引)

    非聚集索引 概述 对于非聚集索引,涉及的信息要比聚集索引更多一些,由于整个篇幅比较大涉及接下来的要写的“包含列的索引”,“索引碎片”等一些知识点,可能要结合起来阅读理解起来要更容易一些.非聚集索引和聚 ...

  4. SQLSERVER聚集索引与非聚集索引的再次研究(下)

    SQLSERVER聚集索引与非聚集索引的再次研究(下) 上篇主要说了聚集索引和简单介绍了一下非聚集索引,相信大家一定对聚集索引和非聚集索引开始有一点了解了. 这篇文章只是作为参考,里面的观点不一定正确 ...

  5. SQL SERVER 索引之聚集索引和非聚集索引的描述

    索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度. 索引包含由表或视图中的一列或多列生成的键. 这些键存储在一个结构(B 树)中,使 SQL Server 可以快速有效地查找与键值关 ...

  6. SQL Server索引 (原理、存储)聚集索引、非聚集索引、堆 <第一篇>

    一.存储结构 在SQL Server中,有许多不同的可用排列规则选项. 二进制:按字符的数字表示形式排序(ASCII码中,用数字32表示空格,用68表示字母"D").因为所有内容都 ...

  7. SQL Server 非聚集索引的覆盖,连接,交叉和过滤 <第二篇>

    在SQL Server中,非聚集索引其实可以看做是一个含有聚集索引的表,但相对实际的表来说,非聚集索引中所存储的表的列数要少得多,一般就是索引列,聚集键(或RID).非聚集索引仅仅包含源表中的非聚集索 ...

  8. SQL Server索引 - 聚集索引、非聚集索引、非聚集唯一索引 <第八篇>

    聚集索引.非聚集索引.非聚集唯一索引 我们都知道建立适当的索引能够提高查询速度,优化查询.先说明一下,无论是聚集索引还是非聚集索引都是B树结构. 聚集索引默认与主键相匹配,在设置主键时,SQL Ser ...

  9. 聚集索引VS非聚集索引

    聚集索引VS非聚集索引 SQL Server 2014 发布日期: 2016年12月 索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度. 索引包含由表或视图中的一列或多列生成的键. ...

随机推荐

  1. 10分钟掌握XML、JSON及其解析

    引言 NOKIA 有句著名的广告语:“科技以人为本”.任何技术都是为了满足人的生产生活需要而产生的.具体到小小的一个手机,里面蕴含的技术也是浩如烟海,是几千年来人类科技的结晶,单个人穷其一生也未必能掌 ...

  2. 微信小程序实例-获取当前的地理位置、速度

    微信小程序官方文档 https://mp.weixin.qq.com/debug/wxadoc/dev/api/location.html JS代码 //index.js //获取应用实例 var a ...

  3. 介绍 .NET Standard

    作者:Vicey Wang 链接:https://zhuanlan.zhihu.com/p/24267356 原文:Introducing .NET Standard 作者:Immo Landwert ...

  4. 关于把本地应用封装成windows app发布审核通不过的问题

    把传统的b/s系统,简单改版,做成了一个比较适合于领导查询的系统,并开发了一个app程序封装了webview直接导向该程序,无需登陆直接访问:结果在提交app的时候审核通不过,问题是安全审核失败: 大 ...

  5. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】【实验一】流水灯模块

    实验一:流水灯模块 对于发展商而言,动土仪式无疑是最重要的任务.为此,流水灯实验作为低级建模II的动土仪式再适合不过了.废话少说,我们还是开始实验吧. 图1.1 实验一建模图. 如图1.1 所示,实验 ...

  6. IBM HTTP Server Performance Tuning

    IBM HTTP Server Performance Tuninghttp://publib.boulder.ibm.com/httpserv/ihsdiag/ihs_performance.htm ...

  7. MFC一个类访问另一个类成员对象的成员变量值

    MFC中一个类要访问另外一个类的的对象的成员变量值,这就需要获得原来那个类对象的指针,其实有好几种方法都可以实现. 比如维护一个单例模式.设置静态变量等等.我们这里举个列子,实现多个类之间的相互访问. ...

  8. 飞思卡尔9S12X系列双核中的协处理器XGATE使用方法

    http://adi.chinaaet.com/analog/blogdetail/24482.html

  9. Js 一些方法(一)

    (function ($) { var promoter = promoter || {}; promoter.utils = (function () { var controller = &quo ...

  10. 严重: Exception starting filter struts2解决方法!

    转自:http://blog.knowsky.com/260578.htm 问题出现: 严重: Exception starting filter struts2java.lang.ClassNotF ...