第8/24周 覆盖索引 临界点

时间过得真快——再过几分钟,你就要完成第2个月的性能调优培训。今天这部分培训我想讲下非聚集索引的更多信息,还有你会碰到它的一些负作用。

上一星期我们讨论了SQL Server里的书签查找,它是非常危险的。在执行计划里SQL Server访问非聚集索引时,额外列必须要从表本身获取时(因为它们不是非聚集索引的一部分),书签查找会发生。如果你想避免书签查找,你可以在SQL Server里定义覆盖索引(Covering Index ) 。我们来看下。

覆盖索引(Covering Index)

在SQL Server里覆盖索引是传统的非聚集索引。唯一的区别是覆盖非聚集索引可以包含给出查询所有需要的列。这就是说使用覆盖索引可以避免书签查找。我们来看一个非常简单的例子。下列的查询会产生书签查找,因为PostalCode列不是非聚集索引IX_Address_StateProvinceID 的一部分,在执行计划里,这个非聚集索引已被使用。

1 SELECT
2    AddressID,
3    PostalCode
4 FROM Person.Address
5 WHERE StateProvinceID = 42
6 GO

这个查询本身产生18个逻辑读。你可以通过定义覆盖非聚集索引,拿掉这个查询的书签查找。就是说,我们需要包含PostalCode 列,在非聚集索引的叶子层。

1 CREATE NONCLUSTERED INDEX idxAddress_StateProvinceID ON
2 Person.Address (StateProvinceID)
3 INCLUDE (PostalCode)
4 GO

当你再次执行这个查询时,从执行计划里你可以看到书签查找已经不见了,SQL Server使用索引查找(非聚集索引)运算符。逻辑读减少为2个。非常显著的性能提升!

唯一你要知道的是,并不是每个书签查找都是非常危险的。我们的目标不是移除每个书签查找,只有坏的才移除。

临界点(Tipping Point)

在一些情况下,当SQL Server对指定查询进行书签查找操作时,它可以决定书签查找太耗资源了(根据必须的逻辑读)。在那个情况下,SQL Server会进行全表扫描,而忽略所有的非合格列。做出这个决定点位置,在SQL Server里被称为临界点(Tipping Point)。临界点就是SQL Server用来决定是进行书签查找还是全表扫描。

临界点躲在你查询需要读取页数的1/4到1/4的某个位置。这和你需要读取的记录数无关(因为记录的大小决定了1页里你可以存放多少记录)。对于这个非常简单的例子,我定义的表里每条记录长度是400 bytes长,这就是在8k的页里可以存放20条记录。另外我在Value列定义了一个非聚集索引。下面的查询使用书签查找返回1061条记录。

1 SELECT * FROM Customers
2 WHERE Value < 1062
3 GO 

如果获取更多一条记录,作为特殊情况的下面查询就会临界点上,然后SQL Server就会扫描整个表。

1 SELECT * FROM Customers
2 WHERE Value < 1063
3 GO

2个近乎一样的查询,却有完全不同的执行计划!这在某些情况下会是个巨大的问题,因为你的计划稳定性不再。过去几年我与很多不同客户打交道时,因为这个问题,它们的SQL Server近乎发疯。

小结

在这一部分的性能调优培训里,你学习了SQL Server里的覆盖非聚集索引还有临界点。在你学习的4个星期里,索引在SQL Server里可以说是个很神奇的东西!

每个索引在提高你读性能的同时,也会降低你的写性能。在你执行INSERT, UPDATE和DELETE语句时, 每个索引都由SQL Server全权负责维护。因此,你要基于读需求和写工作量来平衡你的索引策略。

接下来的4个星期,我们会聚焦更多SQL Server里的执行计划,你会学到如何读懂和理解执行计划,还有它们如何用来做性能调优。请继续关注,下周见!

时间: 2024-11-05 14:59:58

第8/24周 覆盖索引 临界点的相关文章

第6周 聚集索引

上个星期我向你介绍了堆表(heap tables).我们说过,在SQL Server表可以是堆表(Heap Table)或者聚集表(Clustered Table)——一个在它上面有聚集索引(Clustered Index)定义的表.今天我们来谈论聚集索引(Clustered Index)的更多细节,还有如何选择正确的聚集键(Clustered Key). 每次你在SQL Server创建一个主键约束(Primary Key constraint),这个约束(默认情况)是通过唯一聚集索引(Uni

mysql高效索引之覆盖索引

概念 如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作 判断标准 使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为using index,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询 注意 1.覆盖索引也并不适用于任意的索引类型,索引必须存储列的值 2.Hash 和full-text索引不存储值,因此MySQL只能使用B-TREE 3.并且不同的存储引擎实现覆盖索引都是不同的 4.并

覆盖索引(covering index)一次神奇的MySQL优化

话说有这么一个表: CREATE TABLE `user_group` ( `id` int(11) NOT NULL auto_increment, `uid` int(11) NOT NULL, `group_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `uid` (`uid`), KEY `group_id` (`group_id`), ) ENGINE=InnoDB AUTO_INCREMENT=750366 DEFAULT CHARSET

mysql覆盖索引(屌的狠,提高速度)

话说有这么一个表: CREATE TABLE `user_group` ( `id` int(11) NOT NULL auto_increment, `uid` int(11) NOT NULL, `group_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `uid` (`uid`), KEY `group_id` (`group_id`), ) ENGINE=InnoDB AUTO_INCREMENT=750366 DEFAULT CHARSET

Sql Server 覆盖索引

覆盖索引通常都是复合索引,即索引字段为多个.创建索引时应该注意索引排列顺序. Sql Server检索应用索引时,字段识别顺序为 从左到右. 例如如下索引的使用上 Create NONCLUSTERED  Index IX_Student_Name_Class_Sex On tb_Student(Name,Class,Sex) Select * From tb_Student as s where s.Name='ws'  //可以使用 Select * From tb_Student as s

MySQL 覆盖索引

通常大家都会根据查询的WHERE 条件来穿件合适的索引,不过这只是索引优化的一个方面.设计优秀的索引应该考虑到整个查询,而不单单是WHERE 条件部分.索引确实是一种查找数据的高效方式,但是MySQL也可以使用索引来直接获取列的数据,这样就不再需要读取数据行.如果索引的叶子节点已经包含要查询的数据,那么还有什么必要再返回表查询呢?如果一个索引包含(或者覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”. 覆盖索引是非常有用的工具,能够极大的提高性能.考虑一下如果查询只需要索引而无需返回表,会

覆盖索引小结

覆盖索引(convering indexes) 通过索引数据结构,即可直接返回数据,不需要回表. 执行计划中,显式关键字 using index. 假设有这样的索引 indx1(id,user,passwd) 覆盖索引会被用到: 1.SELECT id,user,passwd FROM t1 WHERE id=? 2.SELECT id,user,passwd FROM t1 WHERE id=? AND user=? 3.SELECT id,user,passwd FROM t1 WHERE

理解MySQL数据库覆盖索引 (转)

http://www.cnblogs.com/zl0372/articles/mysql_32.html 话说有这么一个表: CREATE TABLE `user_group` ( `id` int(11) NOT NULL auto_increment, `uid` int(11) NOT NULL, `group_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `uid` (`uid`), KEY `group_id` (`group_id`),

MySQL之高效覆盖索引

mysql中的一种十分高效有用的索引---覆盖索引. 覆盖索引用通俗的话讲就是在select的时候只用去读取索引而取得数据,无需进行二次select相关表.这样的索引的叶子节点上面也包含了他们索引的数据. select * from table_name: select id,name from table_name; 在多数情况下,我们只应该去查询我们有必要知道的列,这样一来网络之间传送的数据包小了,减少了网络通信,你查询的速度自然会得到提升. select a  from table_nam