黄浩 2016-11-16 10:24:08

作者介绍

黄浩,现任职于中国惠普,从业十年,始终专注于SQL。在华为做项目的两年多,做过大大小小的SQL多达1500个。闲暇之余,喜欢将部分案例写成博客发表在华为内部数据库官方社区,反响强烈,已连续四个月蝉联该社区最佳博主。目前已开设专栏“优哉悠斋”,成为首个受邀社区“专家访谈”的外协人员。

公元2016年8月1日晚上,朋友圈流行着这样一个段子:特想摸清台风“妮妲”的威力有多大,一专业人士说:只须一句话就能让你深刻理解。遂追问,答曰:“就连华为都通知放假了?”感谢“妮妲”,让深圳这座高速运转的城市在星期二这天暂停了;感谢华为,让我这个来深10年,为生活奔波劳顿的人也能倚在窗前,眼观疾风骤雨之变,心游惊涛骇浪之中。

妮妲走了,SQL来了

8月3日,一同事转来一个SQL,我打开文件,发现整个代码多达347行。


在DB中执行,时耗达到了4分多钟,再往下钻取,如同蜗牛一般,根本钻不动,14分钟过去了,还只钻取到了800行。

由此该SQL的性能表现为“两慢”:首条返回慢、下钻提取慢。大多数情况,我们只会遇其一,要么快速返回出现性能瓶颈,要么全部提取出现性能瓶颈。这回好了,都齐全了。透过窗户,望着被“妮妲”肆意狂虐后叶颤枝乱的树木,心里不禁在想:服务器也被“妮妲”肆虐了?

此时,台风“妮妲”疯狂过后的温馨凉意,也没能让我心如止水,毕竟这个优化任务看起来有些棘手。

人生若只如初见

因为来者不善,而时间宽限,我也计划打持久战。在展开分析前,我对SQL中的表对象和数据量做了初步统计。如下:

人生若只如初见,初见往往是美妙的,让人心旷神怡的。而与该SQL的初次交流,画面却是暗潮涌动杀机四伏:

  1. 动辄千万上亿的数据量,近40次对象访问,还不包括VIEW中的表对象。

  2. 从SQL代码上看,出现了聚合函数,因此可以断定是批量数据处理。

以上两点,按经验,能2分钟跑出来就不错了,现在是要求2~3S,看起来是一个不可完成的任务。

第一板斧:大刀阔斧

在初步分析中,ORDER_RELEASE和ORDER_RELEASE_REFNUM两个表是最抢眼的,数据量分别是千万级和亿级,访问次数更是惊人的达到了10次以上。好奇心我决定以这两个表为切入口,探究下是如何被访问的?

借助于NOTEPAD++编辑神器,很快定位到了这两个表的访问情况:

初步一看:

  1. 这两个表的访问基本上都是在子查询中,而且都是成对出现

  2. 仔细对比了子查询后,发现这些子查询可分A、B两类

  3. A类子查询共有5个的代码都是完全一样的,如下:

4、B类子查询共有3个的代码都是完全一样的,如下:

深入子查询内部,无论是A类子查询还是B类子查询,ORDER_RELEASE R和ORDER_RELEASE_REFNUM O_REF的关联方式都是一样的,关联字段是ORDER_RELEASE_GID。此时,结合两个表的命名,按多年的经验,我猜想:

  1. ORDER_RELEASE_GID为ORDER_RELEASE表的主键字段

  2. ORDER_RELEASE_REFNUM与ORDER_RELEASE表存在主外键约束,字段就是ORDER_RELEASE_GID

为了验证我的假设,我VIEW了ORDER_RELEASE_REFNUM的表结构,如下:

果真如此。那么问题来了,即便如此,我们又能做什么呢?答案很简单,这两类子查询中,ORDER_RELEASE表可以被“砍掉”。等价的SQL如下:

A类:

B类:

再看看这个子查询的数据量:

只有8千多条,相对于千万上亿,已经是非常少的数据量了。

结合上述分析结果,我对SQL做了如下调整:

  1. 将A、B类子查询用两个with子查询代替,这样就能减少大表的访问次数;

  2. 在A、B类子查询中,将ORDER_RELEASE表“砍掉”,减少表关联带来的IO开销;

  3. 由于子查询的数据量非常小,将之前的IN子查询改写为INNER JOIN,这样就可以形成小结果集驱动大表的效果。

调整后的代码如下:



对于这次的优化,我并没有抱什么希望,因为这仅仅是常规性的精简,还没有深入到代码内部。或者说,这还仅仅是规范性改写。

果真,执行仍然需要耗时4分多钟,但是,这次的精简并不是没有任何收益。因为当往下钻取时,速度非常快,钻取完6625条记录不到10S。

不知不觉中,已到了下班时间。台风过后,残阳西下,不再燥热,难违暖意,别有一番韵味。

第二板斧:披荆斩棘

第二天一上班,就开始接着昨天的节奏继续优化。

SQL的精简并没有为快速返回带来任何收益,我决定看下执行计划,尝试着从执行计划中得到更多的信息。果真,F5后看到的执行计划中,一个VIEW的COST犹如“鹤立鸡群”,特别的扎眼:

从执行计划看,Oracle对这个视图做了传统的处理,没有合并,也没有谓词推入。所以视图中的表基本上都是table access full。此时,突然想起在当时统计表对象的时候,记得只有一个视图,而在昨天在精简B类子查询的时候,也出现过一个视图。那这两个视图应该是同一个了。而昨天B类子查询的速度是非常快的。

我赶紧将执行计划定位到了B类子查询,如下:

原来如此,在B类子查询中,该视图被merge了。

受此启发,我也计划将主查询中的VIEW通过HINT进行MERGE,但是HINT似乎并不生效,始终都无法改变现有的执行计划。无奈之际,只有深入SQL,实地窥探这个VIEW到底“何德何能”,会让ORACLE优化器如此“死心塌地”的“维持原判”。

从上图中可以看出,该视图与A类子查询进行了关联,而事实上,B类子查询就是该视图与A类子查询关联的结果呀。怎么在这里又要临时关联呢?难道昨天做精简的时候还存在漏网之鱼?

再看代码:

原来这里需要获取该视图的两个字段,而在B类子查询中,我们只获取了SHIPMENT_GID一个字段。那是否可以直接在B类子查询中加一个字段呢?

我们再来看看B类子查询的代码逻辑:

在这里,我们获取了SHIPMENT_GID字段,并对该字段通过DISTINCT去除了重复值。这样做的目的在于,在后面调用该子查询时,以该子查询为驱动表,驱动关联其他表对象。因为子查询的结果集很小,而被关联的表对象都是千万上亿级别的。

很显然,如果我们在B类子查询中增加ORDER_RELEASE_GID字段,就会影响到SHIPMENT_GID的唯一性,这样,在后续的关联查询中,就不能直接用B类子查询驱动关联。这会直接破坏掉已经建立好的驱动关系。

既然增加字段之路行不通,那就尝试着再增加一个WITH子查询,代码如下:

与此同时,对访问该视图的代码也进行了适应性的修改,修改后的脚本如下:




再次执行,耗时2:28,虽然与秒级的性能要求相距甚远,但是至少性能提升了近50%,其意义并在于提升的效果,而在于证明了优化方向是正确的,即在大表林立群狼环视虎视眈眈的环境中,要快速准确的定位出驱动表,需要明确将驱动表数据准备好。

第三板斧:神工鬼斧

性能尚未达标,优化仍需继续。

先看看执行计划:

从COST列,并没有看到成本特别高的操作。所以,我放弃了继续在执行计划上做文章,转而深入分析SQL代码逻辑。

经过一番抽丝剥茧起承转合后,SQL的整体代码逻辑也呼之欲出,发现顶层的逻辑设计非常简单明了,就是三个子查询的结果集内连接,如下图所示:

接下来,我做了一件被人“鄙视”的小儿科的事,就是分别执行了这三个子查询。原本想着总会有一个慢的,我就重点优化慢的那个子查询。而结果却出人意表,三个子查询都是在2S左右就能完成执行,而且数据量都在1万以内。那为何三个子查询关联在一起,性能会如此受影响呢?要知道,如果是三个1万以内的表关联,即便是无任何索引,那也是秒出呀。

那么问题出在哪里呢?没的说,肯定是执行计划并没有按我们预想的去执行这个SQL。此时,我也没有心思去仔细分析执行计划,而是直接祭出了第三板斧通过with子查询的方式将ORDER_REL、SHP、REL三个子查询封装成结果集,改写后的SQL如下:





再看执行计划:

看起来与我们预期的效果一致了,而关键还是要看执行的效率。

3.5S,再往下钻取,也不到10s皇天不负有心人,终于可以画“句号”了。此时,已经是第三天上午,距离拿到原始SQL将近2天的时间了。台风“妮妲”早已销声匿迹,来也匆匆去也匆匆。你方唱罢我登场,立秋前的烧烤模式再次以胜利者的姿态,歇斯底里的“蒸烤”着这片大地。而躲在空调房的人类,也在尽情的透支着地球赐予的有限资源,最终会如同这个SQL一样,终有一天会引发灾难;而再去治理,再去挽救,需要花费更多的资源与精力。

后记

从4分钟到3.5S,从钻取卡顿到一泻千里,整整经历了近2天时间,耗时之长在以往的优化案例中实属少见。事实上,当一开始拿到这个SQL时,尤其是在了解到这个SQL及背后的数据环境时,我心里面是直打鼓的。可以说,是硬着头皮拿下了这个SQL,现在回想起来仍然后怕。然而,除了后怕,更多的是该案例优化过程中所体现出的SQL(优化)精髓:精简之道、驱动为王、集合为本。

精简之道

大道至简、简单即高效、复杂的事情简单化等等这些我们喜闻乐见的生活常识,同样适用于SQL(优化)。记得SQL优化大师曾说过:不要让ORACLE做多余的事。而对于ORACLE而言,多余的事情是什么呢?多余的表关联、重复的表访问、冗余的关联(过滤)条件、不必要的DISTINCT\ORDER BY\GROUP BY、曲折的访问路径。虽然ORACLE优化器引擎也在努力识别并消除这些“多余的事 ”(可参见博客,然而,在面对复杂的SQL时,ORACLE也往往束手无策。因此,SQL优化的首要之事就是精简SQL。

驱动为王

有这样一句话:一头狮子领着一群羊,要胜过一头羊领着一群狮子。这就道出了“领头”的重要性,在ORACLE优化器中,就是“驱动表”。驱动表的意义有如木楔子,只有薄如纸片锐如刀刃的楔子,才能轻而易举的插入坚硬木桩中。如果给你一个圆头的木头,任凭你力气再大,也不能插入。这就要求驱动表的数据量要足够的少。尽管ORACLE优化器也在努力寻找合适的“领头”,而有的时候,ORACLE优化器会被腰里别了杆枪的老鼠给骗了。比如本案例中的A类子查询,起初是通过IN子查询进行过滤的,这就存在很大的性能风险。关于驱动表的优化案例有很多,后续会专题分享。

集合为本

集合操作是二维关系数据库引擎在数据处理时的根本,单表是一个集合,多表关联后的结果也是一个集合,视图、子查询的返回结果还是一个集合,整个SQL执行完后的结果仍然是一个集合。

因此,一个高效的SQL一定有一个合理的集合运算结构。根据业务需求,结合代码逻辑,有的时候需要将代码片通过子查询封装;而有的时候又需要将子查询合并到主查询中;有的时候需要将大集合根据业务逻辑切片成多个小的集合;有的时候又需要将若干个小的集合预先合并成大集合。总之,在进行SQL(优化)时,一定要有集合的概念,用集合的思维指导SQL(优化)。

SQL优化三板斧:精简之道、驱动为王、集合为本相关推荐

  1. 【MYSQL优化之道摘抄】mysql常见的SQL优化方法

    前言 无论对于开发人员.运维人员或者测试人员,数据库的优化都是绕不开的话题.而在数据库的优化中SQL的优化又是最为关键的一项.再牛逼的服务器硬件也扛不住百万次的oracle硬解析,再快的SSD硬盘和r ...

  2. MySQL高级(SQL优化)

    MySQL高级 一.字符集 1.1.4个级别的字符集 1.2.字符集小结 1.3.字符集与比较规则 1.4.请求到响应过程中字符集的变化 二.SQL大小写规范 2.1.Windows和Linux平台区 ...

  3. SQL优化之六脉神剑

    SQL优化之六脉神剑 DBAplus社群 | 2016-02-03 07:47 本文作者通过身边的案例,详细阐述了SQL优化过程中的种种方法和小窍门,内容丰富且言之有物,希望能让接触到SQL的同学可以 ...

  4. 数据库应用+SQL优化+Git

    数据库应用 1.1 概念 1.1.1 什么是数据库 1.1.2 关系型和非关系型 1.1.3 关系型数据库 1.2 Mysql数据库 1.2.1 MySQL数据存放在哪里? 1.2.2 MySQL服务 ...

  5. 从一条巨慢SQL看基于Oracle的SQL优化(重磅彩蛋+PPT)

    本文根据DBAplus社群第110期线上分享整理而成,文末还有好书送哦~ 讲师介绍 丁俊 新炬网络首席性能优化专家 SQL审核产品经理 DBAplus社群联合发起人.<剑破冰山-Oracle开发 ...

  6. 中秋节,送上一次非常有趣的SQL优化实战经历

    点击上方"搜云库技术团队",选择"设为星标" 回复"1024"或"面试题"获取4T学习资料 补充:看到好多朋友后台留言说 ...

  7. 15 种 SQL 优化中,老司机才懂的处理技巧

    前言 SQL优化是一个大家都比较关注的热门话题,无论你在面试,还是工作中,都很有可能会遇到. 如果某天你负责的某个线上接口,出现了性能问题,需要做优化.那么你首先想到的很有可能是优化SQL语句,因为它 ...

  8. sql 双分区查询_优化案例 | 分区表场景下的SQL优化

    导读 有个表做了分区,每天一个分区. 该表上有个查询,经常只查询表中某一天数据,但每次都几乎要扫描整个分区的所有数据,有什么办法进行优化吗? 一.待优化场景 有一个大表,每天产生的数据量约100万,所 ...

  9. mysql sql优化_浅谈mysql中sql优化

    说到sql优化,一般有几个步骤呢,在网上看到了一篇很不错的帖子.在这分享一下吧,也是自己学习的一个过程. 一.查找慢查询 1.1.查看SQL执行频率 SHOW STATUS LIKE 'Com_%'; ...

最新文章

  1. 从零开始入门 K8s | 理解容器运行时接口 CRI
  2. zookeeper+kafka集群安装之中的一个
  3. 什么时候你应该用JSON Web Token
  4. 开启进度比的文本为什么没有显示_没有开通智钻,为什么会显示流量是在智钻进店的呢?...
  5. 使用URI设计松散耦合的Metro应用程序
  6. mach-o hook
  7. android 服务设置在哪里,如何在Android中为我的服务设置“控制面板”?
  8. C#员工考勤管理系统源码 考勤工资管理系统源码
  9. 【Linux网络配置】
  10. 数字信号处理 史林 课本答案(汇总)
  11. Unity3d 周分享(22期 2019.8.30 )
  12. PEST分析顺丰服务需求_快递行业宏观环境PEST分析
  13. 如何有效地学习知识,如何才能全面发展?
  14. 海航金鹿公务机队规模稳居亚太之首;奥森多生物科技创新中心在沪揭牌 | 美通企业日报...
  15. 数据结构 单词查找 二分法-索引表-Hash表
  16. 利用pandas模块实现excel多行文本合并
  17. [计算机网络] CSMA/CD 协议限定数据帧最小长度为 64B 不变,根据给定的数据传输速率计算得到争用期长度
  18. BugKu--------你必须让他停下
  19. coreldraw橙子怎么画_智慧职教云课堂APPcoreldraw服装款式图绘画(安徽职业技术学院)章节测验答案...
  20. 微软的免费杀毒软件!

热门文章

  1. 李东学计算机在哪学的,被人遗忘的果郡王,从《甄嬛传》出来的李东学如今只能奋进的旋律...
  2. C语言实现offsetof
  3. 红外热释电处理芯片BISS0001应用资料
  4. 数控编程是数控加工的重要步骤
  5. C#中装箱和拆箱的作用 the effection of boxing and unboxing
  6. 回归初始互联网手机核心档位初心 荣耀Play性能真旗舰再出发
  7. 树莓派Zero,Zero W,3B,3B+,4B参数
  8. 【DB笔试面试592】在Oracle中,表和表之间的关联方式有哪几种?
  9. AWS Nitro架构简介
  10. 每周一篇 文献阅读—paper 5