一次耐人寻味的SQL优化:除了SQL改写,还要考虑什么?

作者介绍

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

 

这是一次值得纪念的优化,值得回忆的内容非常丰富,虽然这个SQL本身并不复杂,几乎是一个相对规范式的SQL,所以,这次优化的重点并不是SQL的改写,而更多的是业务需求、物理模型的优化。在长达3个月,历经5个版本的优化过程中,也不泛优化与开发、功能与性能、测试与开发间关系的微妙变化,其间各方的博弈也耐人寻味。

 

事出有因 
 

系统存在一个功能,“编辑日志查询”,顾名思义就是查询被修改的历史记录(这个功能的存在性有待商榷)。功能刚上线的时候,由于数据量少,相安无事,使用甚欢。由于基本上都是大批量的编辑,导致了日志数据量急剧增长,每天的增量大概在100万左右,两个月后,数据量无情的增长到了6000万,性能隐患也日益凸显,终于换来了一封来自业务用户的邮件,于是我也收到了一个需要优化的SQL,如下:

 

 

大胆假设,小心求证 
 

我做SQL优化有个习惯,拿到SQL的时候,并不急于去看执行计划,而是先要快速浏览一遍SQL,一看结构,二看内容,因为我坚信80%的性能问题是由于SQL写法不当导致的,比如冗余的对象访问、冗余的关联条件、冗余的过滤条件、无意义的DISTINCT\UNION\GROUP\ORDER、自定义函数等等这些常见的问题。这个SQL也不例外,很明显TimeZone_Date_Translator这个自定义函数可能会是性能瓶颈。

 

为了验证自己的判断,我将函数去掉后,再执行,果然性能得到了质的提升。因为符合了性能指标,我也没有深入的分析原因。

 

问题严重化 
 

逝者如斯,RP_PLAN_LOG_T表的数据量日复一日的增加,一个月后,又收到了一封来自一线业务用户的邮件,这次的邮件内容措辞相对上一封,要严厉了很多,大意是:该功能的性能问题已经严重影响到一线业务效率,查询数据居然要等待30s之久,更有甚者直接超时报错(120s),因此强烈要求该功能的性能要在5s内。

 

这封邮件犹如一颗巨石,在平静的水面炸开了锅。

 

SQL还是那个SQL,我在PL SQL里面执行,平均耗时在10秒内,也没有邮件中说的30s之久呀。难道是执行计划的走偏导致的?因为这是动态拼凑的SQL,SQLID变化无常,所以分析当时执行计划是否走偏的难度很高。

 

办案讲究的是犯罪现场,而现在“犯罪现场”肯定是不存在的了,那能否可以重现“犯罪现场”呢?虽然此种方案也并不能支撑“执行计划走偏”的原因分析,但是至少可以为我们拓展思维:会不会是查询条件变化?会不会是网络原因?

 

于是,根据邮件里面零碎的信息,我们在生产环境的功能界面上重现了“现场”,但是结果并没有“犯罪”,也就是说并没有出现邮件中说的达到30s之久。因为是根据只言片语拼凑的“现场”,所以可能存在模拟失真的可能性。

 

为了模拟的真实性,我们联系上了“案发”当事人,在询问了“案发”条件后,才得知:原来用户是在选定某个“项目编码”下查询条件下检索了近一年的日志数据。而由于该日志功能才启用了不到4个月,也就是说是查询了某个项目下所有的日志数据。根据用户提供的信息,我们在PL SQL中执行了SQL,确实达到了30s之久,结果数据集的量也达到了500万+。

 

至此,我们可以得出这样的结论:本次查询的性能问题的原因归结于数据量,基表的数据量(近一个亿)及结果集数据量(500万+)。

 

那么,如何解决呢?一方面是如洪水般迅猛增涨的基表数据,另一方面是超大的结果集返回。针对这两个问题,我给出了如下的解决方案:

 

  1. 引入表分区技术,即将基表RP_PLAN_LOG_T表按照operate_time字段按月分区,以实现数据的分区命中
  2. 为实现数据的分区命中,在查询界面将operate_time作为必选条件,而且尽量做到不跨月
  3. 为配合1、2两点,创建project_number和operate_time的联合索引

 

事情往往是从扯皮开始的 
 

现在,问题来了,这些事情谁来落实呢?先说第二点吧,这是改需求呀,需要与BA协商,找到了BA,BA说自己也做不了主呀,还得要跟业务用户去确认,这一来二往的,开发人员性子急,就不耐烦了:还是不改了吧,太麻烦了。

 

再说第一点,数据分区的责任定位也不明确,开发人员说这需要DBA来做,DBA又说这属于应用范畴,理应开发人员写脚本,他们负责执行就好了。扯来扯去,最后又把BA扯出来了:这个事情需要时间来做,BA应该下个需求单,有了需求单,就能评估人天,这样有人天了,自然就有人来做了。

 

而BA也在为自己辩护:这属于纯技术范畴,与业务需求无关,说白了是当初在设计模型的时候就该考虑分区技术,因此这个需求单不能下。

 

最后,开发、BA、DBA、用户及我达成协议:分区由DBA来实施,不过需要在下个版本实施;用户确认可以将operate_time作为必选条件,并且尽量做到压缩查询周期;开发人员在project_number和operate_time字段上创建联合索引。

 

自查的勇气 
 

为了避免用户由“怨责”转变成“投诉”,项目组对该功能的性能也重视起来,要求性能测试人员严格把关,如果性能超过5S就不放行。这样,开发人员就开始对该功能的性能自检自查,测试人员也在积极的准备数据做性能验证。我的责任还是对SQL进行分析并优化。

 

第一次是粗略的过了一遍SQL,发现了TimeZone_Date_Translator自定义函数;第二次直接是优化了对象模型;这一次才是真正的正面又深入的打量这个SQL,其中一段代码引起了我的兴趣:

 

这段代码是获取字段subtitlename值的标量子查询,从代码看,该值的获取逻辑如下:

 

 

以operate_type为“其他”为例,在展开之前,我们先看看相关的模型结构。

RP_PLAN_LOG_T的模型如下:

 

该模型中有个BUSINESS_ID的字段,这个字段存放业务ID:属性类型(即OPERATE_TYPE=1)对应的是RP_PLAN_EXTENSION_T.PLAN_EXTENSION_ID,其他类型(即operate_type in(2,3,4,5,7,8,9))对应的是RP_TASK_T.TASK_ID,Site Owner(即operate_type = 6)为-100,所以,在获取字段subtitlename值的时候需要根据operate_type的值分别到不同的表中获取对应的name值。

 

我们再看看RP_TASK_T和RP_PLAN_EXTENSION_T的模型结构
 

先看RP_TASK_T表模型:

 

 

模型中TASK_ID是主键,但是如果你按照常规理解TASK_ID与TASK_NAME存在一对一的关系的话,那你就错了,这也是玄机所在。在RP_TASK_T表中,TASK_NAME与TASK_ID是一对多的关系,即同一个TASK_NAME对应多个TASK_ID。

 

事实上,TASK_NAME作为一个实体,也是存在一个独立的模型,即SDS_ACTIVITY_T,其结构如下:


 

在这个模型里,ACTVITY_NAME就是对应RP_TASK_T中的TASK_NAME,并且该模型里面的ACTVITY_ID与ACTVITY_NAME在同一个project_number下是一一对应的。
   

看到这里就清晰了,原来,为了获取subtitlename字段值,我们还可以从SDS_ACTIVITY_T表中拿ACTVITY_NAME字段,如果在RP_PLAN_LOG_T表中存放了ACTVITY_ID字段值的话。

 

也就是说,目前operate_type in(2,3,4,5,7,8,9)的情况下,有两种途径可以获取到subtitlename字段值。而这两种途径的优劣在哪里呢?我们对比下rp_task_t和SDS_ACTIVITY_T表的数据量就知道了:
 



存量


增量


RP_TASK_T


2千万+



RP_TASK_HIS_T


420万+



SDS_ACTIVITY_T


11万+


 由此可见,两种途径孰优孰劣显而易见。
   

无独有偶,operate_type = 6的情况与此同出一辙,也是存在另一种通过小数据量的表获取subtitlename的途径。

 

正当其时,测试人员提了一个性能BUG单,内容是:当选择了subtitlename查询条件时,查询响应非常慢,达到了20s之久。这也印证了我的分析:当前获取subtitlename字段值是一个潜在的性能瓶颈。

 

说是潜在的,原因是如果该字段不作为查询条件,则不会触发,因为该SQL的结果集是分页的,每次只返回15条数据,而作为标量子查询,也就是执行15次而已;但是,一旦作为了查询条件,则执行的次数则是巨大的,而标量子查询中的表都是千万级的大表。所以就成为了严重的性能瓶颈。

 

至此,我以BUG单为契机,适时的提出了优化方案:将business_id拆分成两个字段,分别存储ACTVITY_ID和ATTRIBUTE_ID。

 

看热闹的不嫌事大 
 

当我提交这个方案时,开发人员甚为激动,倒不是因为有了方案而激动,而是因为这个方案于他们而言有点不太靠谱,他给出了如下理由:

 

  1. 增加字段,这是伤筋动骨之举,只能在万不得已的情况下才能实施;
  2. 该表的数据并非一个来源,表结构改动后,会涉及到多个来源的代码同步修改;
  3. 目前RP_PLAN_LOG_T表的数据量已经上亿了,增加字段,就意味着需要对历史数据进行初始化,动作太大;
  4. 现在已接近版本上线日,如此大动作在短时间内完成,风险太大。

 

其实,说白了,就是这个版本的工作计划无法承受该方案,所以站在他们的立场,目前正在如火如荼进行版本的功能开发,这是优先保障的,而我的方案被当成了:看热闹不嫌事大;但是如果是在原有模型的基础上,我实难完成优化目标。

 

时间一天一天过去了,待到上线前一天,这个BUG单依然open着,而按照上线变更条例,如果有BUG单没有close掉,是不能上线的。最后关头,在测试人员的紧逼之下,开发的SE动用了“特权”:将该BUG单移至到下个版本。尽管测试人员强烈反对,但是功能优先性能的大条件不容挑衅。

 

断腕的决心 
 

测试人员上个版本吃了哑巴亏,在版本上线后,第一时间盯着开发人员优化该功能。毕竟头上悬着业务用户这把利剑,开发人员也不敢马虎,也投入了人力优化。方案很简单,但是对于整个功能代码而言,涉及到的内容就远比在表上增加两个字段复杂得多,从如下邮件截图可窥一斑:

 

 

总结 
 

该功能的性能优化在经历了自定义函数、分区、索引、业务方案、模型方案后,性能总算是稳定了下来,但是仍然留给了我很多疑问:

 

  1. 模型设计初期是否能考虑全面?是否能做到一步到位?
  2. 日志查询的意义何在?一次性查询十万百万的数据意义何在?
  3. 能否有一套成熟的方案来应对查询条件的动态化?查询条件是动态组合的,显然索引不可能动态组合。

 

本文就先到这。关于以上疑问,后续会另写文章继续分享。


时间: 2024-10-28 11:21:14

一次耐人寻味的SQL优化:除了SQL改写,还要考虑什么?的相关文章

sql 优化 嵌套-SQL 查询优化 嵌套查询

问题描述 SQL 查询优化 嵌套查询 sql server sql如下:select *from ( select subjectCOUNT(Subject) as cout from Questions where UserID ='banianji' and AskDate>'2011-01-01' and AskDate<'2014-01-01' group by Subject ) as a where a.cout= ( select MAX(cout)from ( select s

SQL优化-同SQL不同执行计划(一)_Mssql系列教程

 问题来源:CSDN Oracle技术论坛 问题        :myepoch提出相同SQL因为检索的值,不同执行效率差别巨大. 问题环境:Oracle 10g,tbnc_adminpowerscope表在adminaccount有B树索引, TBNC_ADMINPOWERSCOPE总记录数:33422行 TBNC_CMSARTICLE总记录数:40782行 结果      :已经解决,优化后的SQL执行时间,均小于0.032秒 [SQL 1]: select art.article_id,

被埋没的SQL优化利器——Oracle SQL monitor

转载声明:本文为DBA+社群原创文章,转载必须连同本订阅号二维码全文转载,并注明作者名字及来源:DBA+社群(dbaplus).    据说,在Oracle企业版数据库中有一个免费的工具,乃SQL优化之利器,那就是Oracle SQL monitor.下面,由DBA+社群原创专家周俊,给大家科普一下这一被埋没的神器.   专家简介    周俊 DBA+社群原创专家   具有14年以上Oracle数据库技术支持经验,在IBM的7年间担任华东区非IBM logo产品技术支持团队team leader

ORACLE性能优化之SQL语句优化

文章来源:http://blog.csdn.net/jdzms23/article/details/23850783 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] SQL语句执行过程 1 SQL语句的执行步骤 2 典型SELECT语句完整的执行顺序 3 SQL语句执行过程 优化器及执行计划 1 SQL优化方法论 合理应用Hints 1Hints 索引及应用实例 1什么是索引 2索引分类 3什么时候使用索引 4改写SQL使用索引 5索引应用 其他优化技术及应用 1其他优化

【重磅干货】看了此文,Oracle SQL优化文章不必再看!

听"俊"一席话,胜读十年书.看了这篇由DBA+社群联合发起人丁俊大师(网名:dingjun123)分享的SQL优化大作,其他Oracle SQL优化文章都不必再看了!   专家简介    丁俊 网名:dingjun123 DBA+社群联合发起人   性能优化专家,Oracle ACEA,ITPUB开发版资深版主.8年电信行业从业经验,在某大型电信系统提供商工作7年,任资深工程师,从事过系统开发与维护.业务架构和数据分析.系统优化等工作.擅长基于ORACLE的系统优化,精通SQL.PL/

oracle sql 优化 ,请各位看看哈

问题描述 oracle sql 优化 ,请各位看看哈 select e.id, e.content, e.write_time, e.orig_id, e.from_client, e.images, u.nickname || '(' || u.realname || ')' as writer, a.orig_content, b.praise_times, c.comment_times, d.transfer_times from wx_essay e left join wx_user

CloudDBA初体验:SQL优化建议

数据库诊断和优化过程具有相当的复杂性,通常需要专业的DBA来解决.但在云计算的今天,人力运维和支撑已经变得不可能,自动化,智能化运维和服务支持日益迫切. 阿里云数据库团队在这方面不断的探索和积累,产出了CloudDBA.其目的就是要把我们已知问题和最佳实践能够以最简单的方式告诉用户,把我们多年使用数据库的经验传承给用户,方便客户使用云上数据库,给客户带来直接的价值.CloudDBA同时也在服务着内部业务,4000+的数据库实例之前需要一个team的运维人员,到现在我们只有一个同学,运维效率大幅提

智能SQL优化工具--SQL Optimizer for SQL Server(帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 )

原文:智能SQL优化工具--SQL Optimizer for SQL Server(帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 ) SQL Optimizer for SQL Server 帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句   SQL Optimizer for SQL Server 让 SQL Server DBA或者T-SQL开发人员能够主动地识别潜在的SQL性能问题,通过扫描和分析SQL语句进行人工智能自动SQL优化.Dell SQL Opt

SQL优化经验总结_MsSql

一. 优化SQL步骤1. 通过 show status和应用特点了解各种 SQL的执行频率     通过 SHOW STATUS 可以提供服务器状态信息,也可以使用 mysqladmin extende d-status 命令获得. SHOW STATUS 可以根据需要显示 session 级别的统计结果和 global级别的统计结果.    如显示当前session: SHOW STATUS like "Com_%"; 全局级别:show global status;    以下几个

看了此文,Oracle SQL优化文章不必再看!

  第一章 看了此文,Oracle SQL优化文章不必再看! DBAplus社群 | 2015-11-17 23:44 目录SQL优化的本质 SQL优化Road Map 2.1 制定SQL优化目标 2.2 检查执行计划 2.3 检查统计信息 2.4 检查高效访问结构 2.5 检查影响优化器的参数 2.6 SQL语句编写问题 2.7 SQL优??\x2F限制导致的执行计划差 SQL优化案例 SQL执行计划获取 4.1 如何获取准确的执行计划 4.2 看懂执行计划执行顺序 一SQL优化的本质 一般来