2019独角兽企业重金招聘Python工程师标准>>>

一个容易被忽略的SQL调优技巧 --- order by字段到底要不要加入索引

字数 3507阅读 1275评论 1赞 2

作者:邱峙

对于SQL调优,要调就调到极致,小编并不是处女座,而是因为在一个并发量很大的业务系统中,对于频繁执行的单条SQL性能的提升,可能对整体数据库的性能提升都有很大的意义。
但是遇到order by字段后面的字段,特别是当这个字段不在过滤条件中时,小编就会心里打鼓,是加到索引里面呢,还是不加到索引里面呢,加进去会不会没有起到提升性能的作用,反而让索引变得更加复杂,给系统带来不必要的额外负担,“偷鸡不成蚀把米”,开个玩笑。但是如果直接忽略掉这个问题,很可能这个提升系统性能的机会就被错过了。
所以今天小编就和大家探讨一下,面对order by字段后面的条件,特别是这个条件不在过滤条件中时,到底要不要加入索引中,对于SQL调优这笔账,索引中加入order by字段,是赚了还是赔了❓

Part 1

空话不多说,先来一个小实验,热一下身。通过多次复制dba_objects中的数据,生成测试表T1,大约1000万行数据。做一个简单的查询,查询T1表中object_id最小的10行数据,select * from (select * from T1 order by object_id) where rownum<=10,耗时‘Elapsed: 00:00:35.92’,执行计划如下:

执行计划中可以看到,先作了一个全表扫,取到了结果集11M行(可以粗略理解为11百万行,这个测试表T1行数为11943842)。然后作了一个排序,截取最小的10条记录,最后返回结果。下面我们在object_id字段上建一个索引I_T1_ORDER3,作一个比较。
耗时从刚才的35秒,直接降到了 ‘Elapsed: 00:00:00.01’,提升性能的效果非常明显。索引和执行计划如下:


从执行计划中可以看到,优化器直接从索引中找到了最小的10条记录,然后回表取得结果集返回。相比上一个执行计划,省去了全表扫描,省去了排序,所以执行时间和系统资源消耗都大大减少。
在这里作一个简单的分析,首先索引和数据不同,是按照有序的排列存储的,当结果集要求按照顺序取得一部分数据时,索引的功效会体现的非常明显,本次查询就是要取得object_id最小的10条记录。其次,建立索引系统只需要消耗一次资源完成排序过程,而如果没有索引,执行不同的语句可能每次都要经历排序的过程,会消耗更多的系统资源。从这个实验看,在order by字段建索引是非常划算的,而且order by字段并不一定非要加入到where条件中也可以生效。

这里小编要和大家分享一个自己踩到的“坑”,就是小编起初在建了索引I_T1_ORDER3后,这条查询语句的执行计划并不选择索引,增加了hint提示也不选择,小编都有点怀疑人生了,明显使用索引会好,为什么优化器偏偏不选择索引呢,而且是加了hint也不走。在修改object_id列为非空属性(NOT NULL)后,优化器才选择了这个索引。小编这里是这么理解的,如果这一列存在NULL值,NULL值是没有大小这一说法的,而且不会被保存在索引中。如果优化器无法确定该列没有NULL值,为了保证结果集的准确性,宁愿选择更慢的全表扫描,也不会选择走可能存在NULL的索引,即使用户指定了hint也不会选择(这里的几句话有点绕,大家耐心读一下)。从这一点来看,开发Oracle优化器的小伙伴是非常靠谱的。

Part 2

上面的实验中order by字段加入索引的作用非常明显。可是在实际生产环境中,能有这么简单的SQL来给DBA调优的机会并不多,实际生产中的SQL往往要更复杂一些。下面我们就把测试变得复杂一点,复制测试表T1,生成测试表T2,查询object_type类似INDEX中object_id最小的10条记录,select * from (select * from T2 where object_type like '%INDEX%' order by object_id) where rownum<=10。
这条语句比第一个实验中多了过滤条件,但是使用了like方法。按通常的经验建索引首先会考虑where条件后的字段,但是在使用like的过滤条件上建立索引,效果可能并不好。可是如果这条语句是业务系统中执行频率非常高的语句呢,我们还是硬着头皮优化一下吧。先看一下没有索引的情况。

执行时间“Elapsed: 00:00:08.75”,接近9s,从执行计划中看到,先是全表扫描过滤出了1597K条(1597K约163万条)记录,然后作了个排序,返回object_id最小的10条记录。
这样的执行效率在生产系统中是不能接受的,但是在like列上建索引效果可能并不好,本着敬业的精神,还是试一下吧。在仅有的两个条件 object_type和object_id上建一个复合索引I_T2_ORDER2,并
加入hint提示,结果如下:


执行时间“Elapsed: 00:00:17.25”,比刚才9秒还多花了8秒。从执行计划中可以看到,先是在索引I_T2_ORDER2中定位到1597K条记录,然后回表取得1597K记录的结果集,再排序取到object_id最小的10条记录。与上一个执行计划相比,反而增加了一个读索引的步骤,所以系统资源消耗更多,执行时间也更长,而且虽然order by字段加入到索引中,并没有省去排序的步骤。在这里这个索引建的就有点亏了。
“理想很丰满,现实很骨感”,看来SQL变得复杂以后,order by字段在索引里面果然不灵了,这招不好使。不要着急,咱们分析一下,为什么不好使了。大家都知道索引是树状结构,现在I_T2_ORDER2索引中有两个字段,这个索引结构大概是这个样子的,如下图。

大家可以看到,对应INDEX节点下面的object_id“3,9,13”是有序的, INDEX PARTITION节点也类似。但是把INDEX节点和INDEX PARTITION节点对应的object_id放到一起,“3,9,13…2,15,17”,就变得无序了,所以优化器虽然使用了索引,但不得不再做一遍排序,order by索引的功效并没有发挥出来。
看到这里是不是有点灰心了,这条语句没法优化了。看下本文的标题,换个角度想一下,说不定这条语句还有救。与测试表T1一样,在object_id上建一个索引I_T2_ORDER3试一下。


执行时间从17s,直接变为“Elapsed: 00:00:00.01”,从执行计划可以看到,优化器通过索引过滤了817条记录后得到了想要的10条结果,之后回表取得结果返回。与上面的执行计划相比,时间消耗和资源消耗都大大减少。
这里我们简单分析一下,索引I_T2_ORDER3是按照object_id有序排列的,当优化器按序处理到817条记录时,就已经得到了想要的object_type类似INDEX,object_id最小的10条记录,然后回表取到结果并返回,省去了全表扫描以及排序的消耗,所以效率大大提升。索引结构如下图。

执行时间和系统消耗,都大大减少,那么到这里我们是不是可以交差了。再看一下我们文章的开头,“对于SQL调优,要调就调到极致”, “对于频繁执行的单条SQL性能的提升,对整体数据库的性能提升都有很大的意义”。我们再想一下还可不可以更优。小编在这里又建了一个索引I_T2_ORDER4,再执行这条查询语句。


执行时间“Elapsed: 00:00:00.01”,从执行计划中可以看到,优化器通过索引直接定位到了想要的10条记录,回表取得10条记录并返回。最终结果只有10条记录,优化器也只处理了10条记录,几乎没有任何的资源浪费。I_T2_ORDER4索引的结构图如下,可以看到,过滤条件已经在索引中存储了,所以优化器可以在索引中直接定位到最终的10条记录。

到这里,从建索引的角度出发,小编认为这条SQL的优化可以交差了。

Part 3

最后小编想说的是,遇到类似order by字段是否加入索引的问题,或者其他一些大家犹豫的问题,可以大胆的尝试,并打开思路,从不同的角度考虑,多做测试,不要错过任何一个提升性能的机会。
对于order by字段加入索引本身这个问题,如果最终的结果集是以order by字段为条件筛选的,将order by字段加入索引,并放在索引中正确的位置,会有明显的性能提升。不过这里要注意小编前面提到的那个坑,order by字段需要是非空的属性,否则会无效。
好了,今天的分享就到这里,大家可以关注我们的专栏。

转载于:https://my.oschina.net/u/1162491/blog/1528857

order by 索引相关推荐

  1. MySql ORDER BY 索引优化

    上一篇:MySql 索引优化 MySql 支持两种方式的排序,FileSort 和 Index ,Index 效率高,它指的是 MySql 扫描索引本身完成排序.FileSort 会对数据使用一个外部 ...

  2. like ,order 使用索引

    http://www.itpub.net/thread-377823-1-1.html 前几天有过讨论. 我的建议就是尽量避免使用"%黄%",改为如下: select ui.&qu ...

  3. mysql order by 索引名字_MySQL如何利用索引优化ORDER BY排序语句

    MySQL索引通常是被用于提高WHERE条件的数据行匹配或者执行联结操作时匹配其它表的数据行的搜索速度. MySQL也能利用索引来快速地执行ORDER BY和GROUP BY语句的排序和分组操作. 通 ...

  4. mysql+order+in索引吗_mysql – 为什么ORDER BY不使用索引?

    这是我的表格: CREATE TABLE `person` ( `id` bigint(10) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT ...

  5. MySQL高级- group by ,order by 索引优化

    一:索引失效 1. 2.最佳左前缀法则 4. 8. 使用覆盖索引解决这个问题. 二.索引优化 1.ORDER BY 子句,尽量使用Index方式排序,避免使用FileSort方式排序 MySQL支持两 ...

  6. oracle order by 走索引吗,oracle order by 索引是否使用的情况

    刚开始在网上看了很多说order by 走索引有两个前提条件 1,ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序. 2,ORDER BY中所有的列必须定义为非空. 下面是测试 ...

  7. mysql order 走索引么_mysq order by 不走索引问题

    问题描述 今天遇到一个问题,在一个报表导出的功能中,需要导出用户的某些记录的所有数据,有些用户有多达10万的记录,导出过程非常慢,需要10多秒.经过排查,发现是查询数据库的时候的问题,查询数据居然用了 ...

  8. SAP CRM One Order关于索引表CRMD_ORDER_INDEX的一些性能问题的分析

    From: Wang, Jerry Sent: Wednesday, March 19, 2014 11:54 AM Subject: FW: custom development in IC sea ...

  9. order by、group by也会使用索引?使用这俩关键字的时候索引什么时间会失效

    目录 写在前面 初始化语句 order by索引分析 总结 group by 写在前面 我们都知道,索引可以提高查询和排序的速度. 所以,order by做排序的时候,显然是可以使用索引的. grou ...

最新文章

  1. 给 Windows 驱动程序安装提速
  2. Flask-RESTful的简单使用
  3. 命令行下执行带参数php
  4. python3打开文件的代码_Python3 对文件操作
  5. MyBatis 通用Mapper 入门教程
  6. uva11584 Partitioning by Palindromes
  7. 数据可视化应用案例:麦当劳和肯德基全国门店分布,谁才是分布最广的炸鸡店?
  8. 小强的HTML5移动开发之路(18)——HTML5地理定位
  9. 牛客 - 合约数(树上启发式合并)
  10. php 1 打印出来,php 怎么强制打印错误
  11. Mac下使用Homebrew安装Sphinx和MySQL
  12. java继承时父类常量覆盖吗_Java父类继承中的static和final用法
  13. SpringBoot实战教程(3)| 整合Thymeleaf
  14. Perl 模块 Getopt::Std 和 Getopt::Long
  15. 计算机科学与技术的班会,计算机科学与技术学院2014级信管三班班会
  16. 中国孩子的micro:bit:TurnipBit自制小乐器教程实例
  17. CommModify 串口监控,串口过滤,串口数据修改模拟,串口数据读写模拟,串口数据修改器 v3.2.3
  18. 用原始代码制作简易的百度页面
  19. JAVA (Graphics2D)解决合成图片失真问题
  20. Python 追加 Excel 数据

热门文章

  1. SAP QM 稳定性研究功能研习系列1 - 稳定性研究总流程
  2. SAP HUM 使用HU02将几个HU打包到一个外层新的HU号码里
  3. 药企巨头辉瑞与家用机器人 Mabu 合作,利用 AI 评估患者行为
  4. 浅析丨AI安防产品发展现状与趋势分析
  5. Facebook的首席技术官:人工智能已用于内容审核,未来会做更多
  6. SAP MM 不常用事务代码之MBSL - Copy Material Document
  7. 340亿美元买下红帽!IBM百年以来最大手笔,云市场翻身之战!
  8. 干货丨一份机器学习的初学者指南
  9. (完全解决)(numba)TypingError: Failed in nopython mode pipeline( Invalid use of Function)
  10. SOR迭代求解线性方程组代码实现