聚焦-移除Bookmark Lookup、RID Lookup、Key Lookup提高SQL查询性能(六)

前言

前面几节都是讲的基础内容,本节我们讲讲索引性能优化,当对大数据进行处理时首先想到的就是索引,一旦遇到这样的问题则手忙脚乱,各种查资料,为何平常不扎实基本功呢,我们由浅入深,简短的内容,深入的理解,而非一上来就把问题给框死,立马给出解决方案,抛出问题,再到解决问题,你GET了没有。

Bookmark Lookup、RID Lookup、Key Lookup定义

一说到这三者,如果对索引研究不深的童鞋估计是懵逼的,什么玩意,我们姑且将上面三者翻译为:标签查找、行ID查找、键查找。标签查找和键查找是一个意思,在SQL 2005之前叫Key Lookup。怎么解释,如何定义呢?首先我们不看定义,直接看下面一步一步解析,如果你实在忍不住,请看园友【永红】的见解,解释还是非常到位。我们简短的说明下此三者概念。

在查询中,我们对返回的列在查询条件上若建立了非聚集索引,此时将可能尝试使用非聚集索引查找,如果返回的列没有创建非聚集索引,此时会返回到数据页中去获取这些列的数据,即使表中存在聚集索引或者没有,都会返回到表中或者聚集索引中去获取数据。对于以上场景描述,如果表没有创建聚集索引则称为Bookmar Lookup,如果表中没有聚集索引但是存在非聚集索引我们称为RID Lookup。看到这里我们就会想法操作如此耗时,还要返回到基表中去获取数据,所以才有了我们本节来移除以上三者来提高查询性能。接下来我们一起来看看。

抛出Bookmark Lookup、RID Lookup、Key Lookup问题

我们首先创建如下表

USE TSQL2012
GO
CREATE TABLE Sales.Orders
(
[orderid] INT,
[shipaddress] VARCHAR(100),
[shipcity] VARCHAR(100),
[shipregion] VARCHAR(100))
GO

接着进行查询

USE TSQL2012
GO

SELECT orderid, shipaddress, shipregion
FROM Sales.Orders
WHERE shipcity = ‘深圳‘

这个不用多讲,没添加任何索引,执行查询计划是全表扫描。接下来我们创建在orderid上创建聚集索引如下:

CREATE CLUSTERED INDEX idx_cls_orderid ON Sales.Orders(orderid)

我们再执行上述查询

此时我们创建了聚集索引,所以此时查询走聚集索引,到这里我们看到情况由全表扫描转换成了索引扫描。我们在查询时一直是带了查询条件的,而对查询条件我们未作任何操作,如果我们此时在查询条件上创建了索引,此时查询的性能又会得到一点改善。我们开始对查询条件创建一个非聚集索引。

CREATE NONCLUSTERED INDEX idx_nc_shipcity ON Sales.Orders(shipcity) 

我们再接着执行查询

我们观察到对查询条件创建了非聚集索引,查询计划会使用非聚集索引查找返回结果,但是对于shipaddress, shipcity, shipregion并不是索引的一部分,此时查询引擎会返回到基表中得到这些数据再返回。这种行为就叫做Bookmark Lookup或者Key Lookup。下面我们就如本文标题一样问题出现来解决问题,移除Bookmark Lookup或者Key Lookup。我们尝试用两种不同的方法来解决。

解决Bookmark Lookup、RID Lookup、Key Lookup问题

创建非聚集索引覆盖索引

我们对查询条件以及检索列创建非聚集索引。

CREATE NONCLUSTERED INDEX idx_all_cover ON Sales.Orders(shipaddress,orderid,shipcity,shipregion)

此时我们对检索列创建了非聚集索引,此时将不会再到数据页中获取数据,而是从索引中直接返回,所以到这里我们算是移除了Key Lookup。但是此时触发另外一个问题,执行查询计划走的却是索引扫描,索引到底是什么呢?我们打个比方,一个索引相当于是数据库中一个本书开始的索引,我们需要快速从书中查找到我们所需要的数据,这个时候书就是我们所说的表。索引扫描意味着要读取表中的所有行,然后返回满足条件的所有数据,当执行索引扫描时,所有行上叶子节点上的所有都会被扫描,这也就意味着索引上的所有行都会被检索一遍而不是直接检索表,和表扫描对比的话,表扫描是直接读取表中数据,所以表扫描和索引扫描还是有一点点不同,而索引查找则是依赖于索引页数据来定位满足条件的所有行,索引查找仅仅只影响满足条件以及页上包含这些满足条件的行,所以说索引查找更加高效。

上述我们稍微讲解了下索引扫描和索引查找,而上述的问题是我们创建了非聚集索引,但是结果执行的查询计划是索引扫描,很是纳闷,对于刚学索引小白的我来说,不知该如何是好,以为是缓存的缘故,清除各种缓存均不好使。于是开始胡思乱想是不是是不是检索列中数据有为NULL引起的,是不是检索列数据重复引起的,尝试了无数次,最终发现某一次居然好使。如下

CREATE NONCLUSTERED INDEX idx_cls_cover ON
Sales.Orders(shipcity,orderid,shipaddress,shipregion)

此时若我们将查询条件进行如下修改。

USE TSQL2012
GO

SELECT orderid, shipaddress, shipregion
FROM Sales.Orders
WHERE shipaddress = ‘深圳‘
GO

到这里我们应该发现了,唯一的区别在于我们创建非聚集索引时的顺序和查询条件不同就会导致索引扫描和索引查找的转换,那么到底什么时候才会执行索引查找呢?我们可以进行如下一般性总结:

索引查找的一般性结论:如果条件中包含WHERE或者ON的话,查询条件必须是位于索引集合列中首位,此时索引查找将会被使用。

此时我们穿插一点内容,上述我们创建了覆盖索引,我们来比较下覆盖索引和默认情况下聚集索引查找的性能开销。

覆盖索引与默认聚集索引性能开销比较

USE TSQL2012
GO
SELECT orderid, shipaddress, shipregion
FROM Sales.Orders WITH(INDEX([PK_Orders]))
WHERE orderid<10364
go
SELECT orderid, shipaddress, shipregion
FROM Sales.Orders WITH(INDEX([idx_noncls_cover_exceptorderid]))
WHERE orderid<10364
GO

从上可知,覆盖索引的开销要比默认主键聚集索引性能开销要好一点,同时我们可以看看如下二者IO代价。

通过上述覆盖索引与默认聚集索引的对比,我们能够有效的减少IO,这一点也是非常明确的,当然下面的INCLUDE索引对比也是另外一种好的方案。

创建INCLUDE非聚集索引

USE TSQL2012
GO

CREATE NONCLUSTERED INDEX [ix_noncls_include] ON [TSQL2012].[Sales].[Orders] (
 shipcity
) INCLUDE (shipaddress, shipregion, orderid)

至此我们用两种方式来移除了Bookmark Lookup、RID Lookup、Key Lookup,通过使用索引和覆盖索引。

既然有如上两种方式,我们应该有所取舍,二者谁的性能更好呢?我们接下来比较上述二者的开销差异。

比较移除Bookmark Lookup等两种方式差异

USE TSQL2012
GO

SELECT orderid, shipaddress, shipcity, shipregion
FROM Sales.Orders WITH(INDEX(idx_all_cover))
WHERE shipcity = ‘深圳‘
GO

SELECT orderid, shipaddress, shipcity, shipregion
FROM Sales.Orders WITH(INDEX(ix_noncls_include))
WHERE shipcity = ‘深圳‘
GO

我们从上所知,二者开销一样,并未有什么区别,当然相信我们更倾向于的是将第二种方式作为解决方案。到这里算是基本结束了,但是还有一个小问题,我们在之前已经创建了orderid的聚集索引,后面在解决方案中我们也添加了orderid的非聚集索引,难道非得添加吗,我们去掉试试看。

CREATE NONCLUSTERED INDEX idx_noncls_cover_exceptorderid
ON Sales.Orders(shipcity,shipaddress,shipregion)
CREATE NONCLUSTERED INDEX idx_noncls_include_exceptorderid
ON Sales.Orders(shipcity) INCLUDE(shipaddress,shipregion)

去除orderid比较二者开销差异:

USE TSQL2012
GO

SELECT orderid, shipaddress, shipregion
FROM Sales.Orders WITH(INDEX([idx_noncls_cover_exceptorderid]))
WHERE shipaddress = ‘深圳‘
GO

SELECT orderid, shipaddress, shipregion
FROM Sales.Orders WITH(INDEX([idx_noncls_include_exceptorderid]))
WHERE shipaddress = ‘深圳‘
GO

由上知,非聚集索引列不需要包含创建了聚集索引的列,那么事实到底是怎样的呢?

结论:其实对于任何非聚集索引列都不需要包含创建了聚集索引的列,因为创建聚集索引的列是非聚集索引集合列的一部分,也就是说只要一个表上的列创建了聚集索引,那么非聚集索引集合列就包含了这个聚集索引。

总结

本节我们比较详细就问题的抛出到问题的解决,从而来提高查询性能,好了,到此结束,我们下节再会。简短的内容,深入的理解

时间: 2024-10-03 14:02:56

聚焦-移除Bookmark Lookup、RID Lookup、Key Lookup提高SQL查询性能(六)的相关文章

07.索引-非聚集索引(3)-Key Lookup &amp;RID Lookup

如果存在聚集索引并且查找的列不在 非聚集索引的键列中 而在没有聚集索引的表中 使用RID Lookup 书签查找可能因为开销过大导致一些查询直接进行表扫描 聚集索引所有数据都在索引中(数据页也是索引的一部分),因此可以直接通过聚集索引找到所有的列 定位到索引,也就意味着数据找到了 但是非聚集索引定位到索引后,需要在根据索引记录的RID或者Key 查找数据行,势必会导致一次数据页的I/O 数据行过多,形成的数据页IO也会非常大 当数据过大,IO反而可能超过直接进行页扫描的开销,因此系统会选择进行页

zabbix------自定义key监控windows SQL Server

1 用了官方的模版,位置在https://www.zabbix.org/wiki/Zabbix_Templates,感觉有点多,而且在添加了{$SQL1}后仍然是no such service,于是决定自己写一点自定义,顺便学习一下,主要参考网上这位大哥的,http://mayulin.blog.51cto.com/1628315/514509/ 2 windows添加监控项一般用Perfcounter,这是一个计数器,用于监控系统的性能参数,给我的整体感觉就是zabbix对于windows有点

Primary Key and Unique Key difference in SQL Server

posted from http://www.sqlservergeeks.com/primary-key-and-unique-key-difference-in-sql-server/ Primary and Unique Key both enforce uniqueness of columns on which they are defined. Then where do they differ? They differ in following way -          A p

memcache 获取key的方法,查询session存储

标题可能有点不切合我文章表达的意思 当刚刚接触memcache的时候我想到: 设置了负载均衡的时候关于session的存储可否用下memcache,所以搜索了相关的信息. 首先,修改php.ini 以下两个配置 session.save_handler = memcachedsession.save_path = "127.0.0.1:11211" 重启 service php-fpm restart 然后写个PHP文件存储下session试试 session.php <?php

调优过程

性能调优 1 百胜表规范 USE ERP_DW GO /* 功能说明: 创建[区域]维度表 修改说明: Create by LY on 2011-09-07 */ IF EXISTS (SELECT 1 FROM SYSOBJECTS WHERE id = OBJECT_ID('Dim_Area') AND type = 'U') BEGIN DROP TABLE Dim_Area END GO CREATE TABLE [dbo].[Dim_Area] ( AreaCode VARCHAR(2

[MySQL Reference Manual] 8 优化

8.优化 8.优化... 1 8.1 优化概述... 1 8.2 优化SQL语句... 1 8.2.1 优化SELECT语句... 1 8.2.1.1 SELECT语句的速度... 1 8.2.1.2 WHERE子句优化... 1 8.2.1.3 Range优化... 1 8.2.1.4 索引合并(Index Merge)优化... 1 8.2.1.5 引擎Pushdown条件优化... 1 8.2.1.6 索引条件Pushdown优化... 1 8.2.1.7 使用索引扩展... 1 8.2.

HashMap源码分析(JDK1.8)- 你该知道的都在这里了

我的csdn博客地址: http://blog.csdn.net/brycegao321 HashMap是Java和Android程序员的基本功, JDK1.8对HashMap进行了优化, 你真正理解它了吗? 考虑如下问题: 1.哈希基本原理?(答:散列表.hash碰撞.链表.红黑树) 2.hashmap查询的时间复杂度, 影响因素和原理? (答:最好O(1),最差O(n), 如果是红黑O(logN)) 3.resize如何实现的, 记住已经没有rehash了!!!(答:拉链entry根据高位b

运维脚本:索引统计

数据库引擎是高度优化的闭环系统,基于执行计划的反馈,查询优化器在一定程度上自动优化现有的执行计划.查询优化的核心是索引优化,数据库引擎通过计数器统计关于索引操作的数据,统计的信息包括:使用次数.物理存储.底层操作的计数,以及缺失索引等,这些统计数据存储在内存中,是数据库引擎执行情况的真实反馈,高度概括了索引的执行情况,有意识地利用索引的统计信息,有针对性地优化现有的业务逻辑代码,调整查询的执行计划,能够提高数据库的查询性能. 一,统计索引的使用次数 在用户成功提交查询语句时,执行计划中每一个单独

索引统计

数据库引擎是高度优化的闭环系统,基于执行计划的反馈,查询优化器在一定程度上自动优化现有的执行计划.查询优化的核心是索引优化,数据库引擎通过计数器统计关于索引操作的数据,统计的信息包括:使用次数.物理存储.底层操作的计数,以及缺失索引等,这些统计数据存储在内存中,是数据库引擎执行情况的真实反馈,高度概括了索引的执行情况,有意识地利用索引的统计信息,有针对性地优化现有的业务逻辑代码,调整查询的执行计划,能够提高数据库的查询性能. 一,统计索引的使用次数 在用户成功提交查询语句时,执行计划中每一个单独