记一次T-SQL查询优化 索引的重要性

原文:记一次T-SQL查询优化 索引的重要性

概述

在一次调优一个项目组件的性能问题时,发现SQL的设计真的是非常的重要,所以写一篇博文来记录总结一下。

环境介绍

这个项目组件是一个Window服务,内部在使用轮循机会在处理一个事件表中的事件,将其转换在对应的任务。性能问题在于,统计下来,这个服务一秒的时间内只能处理完成12条左右。这个性能是非常的差。

我使用的SQL版本是SQL 2012,机器是CPU I7-2670,内存16G,SSD硬盘。

在这个数据库中有一个表的数据量大概30万条数据,并不是很多, 事先没有建立任何索引,只有一个主键的索引。

那么在这其中有一条非常简单的查询语句:

SELECT TOP 1 *
FROM SMS_SHORTNO_ASSIGN
WHERE
	APP_CODE = ‘SMSNotice‘
	AND IS_DYNAMIC_ASSIGN = ‘N‘
	AND SMS_TYPE_CODE = ‘Mas‘

有数据和无数据的性能对比

在上面的查询中,IS_DYNAMIC_ASSIGN = ‘N‘是查询不到任何数据的,IS_DYNAMIC_ASSIGN = ‘Y‘是有数据的,对比一下,在没有任何数据的情况下,查询是非常的慢,但是有数据的情况下,就不同了。

首先来看一下这个SQL的查询计划是什么样子:

下面是更清晰的执行查询计划:

可以看到,在没有索引的情况下,会执行表扫描。

来看一下各自的执行时间:

可以查询到数据:


不能查询到数据:

可以看到,在没有查询到数据的情况下,总共需要耗时89ms. 不要觉得89ms才只有0.1s都不到,但是想一想之前上面说的1S钟才处理12条记录,就可以想像到和这个89ms有相当大的关系,如果只执行这一条SQL,那么1S钟也只能执行12条左右。

在这种情况下,我们来优化一下这条SQL语句。首先这句SQL本身已经是最简单的,不能再简化,那么只有在索引上下功夫。

聚集索引和非聚集索引

两者之间有什么区别呢?大家可以参考一篇博客圆另一博主的博文 聚集索引和非聚集索引(整理)

首先我们按照我们一般没有深入研究过索引童鞋们的思路,就是把WHERE后面条件的字段加起来建一个索引。

根据WHERE 条件字段创建非聚集索引

创建后好,我们来看看上面的语句的查询计划:

咦,为什么还是使用了表扫描呢,而不用使用索引呢?  

在这里贴上一篇博文 Select * 一定不走索引是否正确? 这篇博文分析了SELECT * 和各种索引的关系,但是这个博文里面分析的和我得出的结论不一样,我也在作者的评论留言了,同时我找到别一篇博文 SELECT * 的真相: 索引覆盖(index coverage) 来解释我现在的现象。因为我不怎么研究SQL,所以我不清楚到底是什么原因,望有知者,可以告知一下。关于索引覆盖也可以参考这篇博文 SQL Server 查询性能优化——覆盖索引(二)

那么我现在将SELECT * 改成 SELECT 字段后,索引才真正的应用了。

可以看到如果SELECT中的字段包含在索引中,将可以利用到索引。

但是这样的话,改变了我原来程序的用意,这是不能接受的。那有什么别的办法可以解决吗?这个时候我想到了聚集索引。

创建聚集索引

默认情况下,在使用表设计器的创建表的时候,会默认创建一个主键的聚集索引。根据主键创建聚集索引,并不一定是最优的选择。关于聚集索引 可以参考下 索引优化(2)聚集索引 。我观察了一下我的表结构,我根据可能使用的列频率最高的两个字段上建立了聚集索引,这两个字段包含在上述语句的WHERE语句中。这两个字段并不是主键。

创建好后,我们再来看一下查询计划和查询的时间:

查询时间:

可以看到,查询速度已经0ms了,非常的快速了。到这里面,其实问题关于这一条SQL优化应该是已经结束了。

聚集索引很重要并且一个表只能建一条聚集索引,不能根据某一条SQL的WHERE来建立,而是要考虑到各种不同的WHERE条件才确定这样建立聚集索引是不是最优的,我根据这两个字段建立好聚集索引后,我使用别的WHERE来查询,速度也是非常的快,所以最后才确认使用这两个字段建聚集索引。

当然我的项目中还是有很多的语句可以优化,以及程序C#代码本身也可以优化,经过我的优化后,处理速度可以达到1秒处理130条左右了。

题外篇

===========================题外篇=======================

在学习这个优化过程中,还有一些别的心得和疑问的,也在此记录一下。

根据上面我创建一条聚集索引就解决了问题,并且也建立了非聚集索引,非聚集索引反而没有用上,那么是不是说非聚集索引就没有用呢?并不是这样的,非聚集索引是SQL优化的很大的一部分。

之前上面说道SELECT中只包含索引列的情况下会使用到非聚焦索引。那么下面再说一个例子来说明非聚集索引的用途。

我们们将之前建立的三个字段的非聚集索引删除,使用统计函数来统计一下符合条件的条数:

查询时间:

可以看到耗时还是很久28ms的. 大家不用关注COUNT(*)可以使用COUNT(1)或Count(主键),这个讨论网上也很多,我自己切换三种写法也没有什么本质的不同。

这时,我们将之前删除的非聚集索引加回来,再来查看查询计划和时间:

可以看到查询计划中,这个时候优先使用了非聚集索引,并且统计的速度是要快过使用聚集索引的。

疑问(求答疑)

在别一个SQL中,也是很简单的SQL,使用了LEFT JOIN后,会导致查询的性能不高,在这种情况下,该如何来优化呢,我使用了not Exists,子查询来各种替换并不能减少这个SQL的查询时间。

业务场景是这样的,SQL还是和之前的一样,SMS_SHORTNO_LOCKED表里面会存入SMS_SHORTNO_ASSIGN表里面的记录,锁定的时候会增加一条,解锁的时候会将这条记录删除,所以在此使用LEFT JOIN来取出一条没有锁定的记录。

下面是它的查询语句和查询计划和响应时间:

这个26ms最主要是在SHORTNO_LOCKED IS NULL这条判断上,如果不是使用IS NULL,而是使用 SHORTNO_LOCKED = 1或=0这种方法来判断的话,查询是非常的快。

那么在此,请问一下大家,相信很多人都使用LEFT JOIN,然后使用IS NULL来判断别一个表没有的数据。但是这样的性能并不是很高,有什么办法可以解决LEFT JOIN的问题,或者可以改成别的写法,我尝试了很多种都没有改善。

所以我想难道以后在设计表的时候,是不是尽量使用 INNER JOIN ,然后根据某一个字段判断特定的值,这样的话,这个字段可以使用索引来优化,像上面就因为IS NULL的问题是没办法使用索引的。

希望有高人指点,谢谢。

时间: 2024-11-08 19:21:28

记一次T-SQL查询优化 索引的重要性的相关文章

sql查询优化 索引优化

sql语句优化 性能不理想的系统中除了一部分是因为应用程序的负载确实超过了服务器的实际处理能力外,更多的是因为系统存在大量的SQL语句需要优化. 为了获得稳定的执行性能,SQL语句越简单越好.对复杂的SQL语句,要设法对之进行简化. 常见的简化规则如下: 1)不要有超过5个以上的表连接(JOIN)2)考虑使用临时表或表变量存放中间结果.3)少用子查询4)视图嵌套不要过深,一般视图嵌套不要超过2个为宜. 连接的表越多,其编译的时间和连接的开销也越大,性能越不好控制. 最好是把连接拆开成较小的几个部

记一次苦逼的Sql查询优化

原文:记一次苦逼的Sql查询优化 最近在维护公司项目时,需要加载某页面,总共加载也就4000多条数据,竟然需要35秒钟,要是数据增长到40000条,我估计好几分钟都搞不定.卧槽,要我是用户的话估计受不了,趁闲着没事,就想把它优化一下,走你. 先把查询贴上: select Pub_AidBasicInformation.AidBasicInfoId, Pub_AidBasicInformation.UserName, Pub_AidBasicInformation.District, Pub_Ai

引用:初探Sql Server 执行计划及Sql查询优化

原文:引用:初探Sql Server 执行计划及Sql查询优化 初探Sql Server 执行计划及Sql查询优化 收藏 MSSQL优化之————探索MSSQL执行计划 作者:no_mIss 最近总想整理下对MSSQL的一些理解与感悟,却一直没有心思和时间写,晚上无事便写了一篇探索MSSQL执行计划,本文讲执行计划但不仅限于讲执行计划. 网上的SQL优化的文章实在是很多,说实在的,我也曾经到处找这样的文章,什么不要使用IN了,什么OR了,什么AND了,很多很多,还有很多人拿出仅几S甚至几MS的时

SQL Server索引设计 <第五篇>

SQL Server索引的设计主要考虑因素如下: 检查WHERE条件和连接条件列: 使用窄索引: 检查列的选择性: 检查列的数据类型: 考虑列顺序: 考虑索引类型(聚集索引OR非聚集索引): 一.检查WHERE条件列和链接条件列 当一个查询提交到SQL Server时,查询优化器尝试为查询中引用的所有表查找最佳的数据访问机制.下面列出查询优化器针对WHERE和连接的工作方式: 优化器识别WHERE子句和连接条件中包含的列. 接着优化器检查这些列上的索引. 优化器通过从索引上维护的统计确定子句的选

SQL Server索引 - 非聚集索引 <第七篇>

一.非聚集索引维护 非聚集索引的行定位器值保持相同的聚集索引值,即使该聚集索引列物理上重新定位后,也是如此. 为了优化这个维护开销,SQL Server添加一个指向旧数据页的指针,以在页面分割之后指向新的数据页面,而不是更新所有相关非聚集索引的行定位器.这样,虽然降低了非聚集索引的维护开销,但是增加了从非聚集索引行到数据行的导航开销,因为添加了一个旧数据页面和信数据页面之间的连接.因此,将聚集索引作为行定位器降低了非聚集索引相关的开销. 二.定义书签查找 当一个查询请求不是优化器选择的非聚集索引

SQL Server 索引和表体系结构(三)

原文:SQL Server 索引和表体系结构(三) 包含列索引 概述 包含列索引也是非聚集索引,索引结构跟聚集索引结构是一样,有一点不同的地方就是包含列索引的非键列只存储在叶子节点:包含列索引的列分为键列和非键列,所谓的非键列就是INCLUDE中包含的列,至少需要有一个键列,且键列和非键列不允许重复,非键列最多允许1023列(也就是表的最多列-1),由于索引键列(不包括非键)必须遵守现有索引大小的限制(最大键列数为 16,总索引键大小为 900 字节)的要求所以引进了包含列索引. 正文 创建包含

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

聚集索引.非聚集索引.非聚集唯一索引 我们都知道建立适当的索引能够提高查询速度,优化查询.先说明一下,无论是聚集索引还是非聚集索引都是B树结构. 聚集索引默认与主键相匹配,在设置主键时,SQL Server会默认在主键列创建聚集索引.但是可以手动更改为在任意一个列创建聚集索引,然后在另一个字段或多个字段上定义主键.这时主键将会被作为一个唯一的非聚集索引(唯一索引)被创建.通过指定NONCLUSTERED关键字就可以做到. CREATE TABLE MyTableKeyExample { Colu

SQL Server 索引和表体系结构(二)

原文:SQL Server 索引和表体系结构(二) 非聚集索引 概述 对于非聚集索引,涉及的信息要比聚集索引更多一些,由于整个篇幅比较大涉及接下来的要写的"包含列的索引","索引碎片"等一些知识点,可能要结合起来阅读理解起来要更容易一些.非聚集索引和聚集索引一样都是B-树结构,但是非聚集索引不改变数据的存储方式,所以一个表允许建多个非聚集索引:非聚集索引的叶层是由索引页而不是由数据页组成,索引行包含索引键值和指向表数据存储位置的行定位器, 既可以使用聚集索引来为表或

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

一.存储结构 在SQL Server中,有许多不同的可用排列规则选项. 二进制:按字符的数字表示形式排序(ASCII码中,用数字32表示空格,用68表示字母"D").因为所有内容都表示为数字,所以处理起来速度最快,遗憾的是,它并不总是如人们所想象,在WHERE子句中进行比较时,使用该选项会造成严重的混乱. 字典顺序:这种排序方式与在字典中看到的排序方式一样,但是少有不同,可以设置大量不同的额外选项来决定是否区分大小写.音调和字符集. 1.平衡树(B-树) 平衡树或B-树仅是提供了一种以