注册
【与达梦同行】达梦数据库SQL优化的一点思考
技术分享/ 文章详情 /

【与达梦同行】达梦数据库SQL优化的一点思考

DM_208879 2022/12/13 5289 27 1

1.引言

在生产系统中关键SQL语句业务逻辑复杂,执行计划冗长,性能至关重要。在拿到优化需求后,如何在有限时间内快速定位到执行计划中最慢的部分,并有的放矢的进行优化呢?本文是作者在近年使用达梦数据库过程中的一点思考,旨在抛砖引玉,相互学习。

2.SQL优化概述

优化器是数据库产品中关乎SQL语句性能的关键组件。DM8采用主流的基于成本(COST)的优化器。简言之,数据库服务器接收到SQL语句后经过一系列转换,借助统计信息和优化器算法,最后评估出一个成本最优的物理执行计划。复杂SQL因为关联的表比较多,表的连接顺序、访问路径、可使用的索引等会有很多的选择。另外,不合适的统计信息采样率或者陈旧的统计信息也会对执行计划产生不利的影响。
优化器会对每条SQL生成多个候选关系树,并计算每棵树的代价(比如SSEK和CSCN操作符的执行成本是不一样的),从而选择总成本最小的关系树。通常优化器不会无限时间的计算下去,而是根据算法选择一种“较优”的执行计划作为当前“最优”的。这是在“时间”和“最优”之间的一种平衡,也是我们经常需要优化慢SQL的原因之一。

3.SQL优化的关键点

发现一条复杂的慢SQL后(通过SQLLOG、AWR、DEM或者业务发现),通常不会直接去理解SQL的业务逻辑,而是先看语句中有多少表、表间的关联关系、Explain的执行计划、SQL的实际执行时间等获取一个直观印象。然后,我们通过SET AUTOTRACE TRACE来获取实际的执行计划。

拿到执行计划后,如何知道慢在哪里就显得举足轻重了,这是SQL优化的“命门”所在。面对一个1000行的单SQL执行计划,如果达梦不能告诉我它慢在哪里,我可能一时都不知道如何下手。启用达梦的ET工具后(grant execute on SYS.ET to User),可以达到这个目的,找到优化的关键点。

ET输出结果是按照TIME(US)升序排列的。举例说明,从下图可以判断最慢的部分出现在第882、927和837步,约占总时间的66.73%。
DM_221213_01.PNG

4.SQL优化手段

4.1创建合适索引

通常,数据库的全表扫描成本(对应达梦中的聚簇索引)比普通索引扫描的成本要高。通过创建合适的索引,甚至全覆盖索引,在只选择表中小比例数据时,可以达到SQL优化的目的。举例说明,一个慢SQL使用ET观测到的结果如下:
DM_221213_02.PNG

该SQL约62.94%的时间消耗在第274步的BLKUP2回表操作上。

首先,一种优化手段是如果where部分条件列和select部分查询列不是特别多,且对性能有很高要求的情况下,可以通过创建全覆盖索引来消除回表操作。
在繁忙时段的生产系统上直接创建索引可能会遇到“锁等待”等问题。生产索引的创建技巧如下:

CREATE INDEX IDX_XVC_THP_CYQAJYDCT_02 on XVC_THP_CYQAJYDCT (C1,C2,C3,C4,C5,C6,C7) UNUSABLE;
ALTER INDEX IDX_XVC_THP_CYQAJYDCT_02 rebuild SHARE;

其中,C1~C5是where条件列,C1的数据选择度最大数据过滤最多,C5的选择度最小。C6和C7是select查询列。

优化效果非常显著,完全消除了CSCNB和LKUP,执行时间从200分钟缩短到70秒。
DM_221213_03.PNG

4.2优化器成本评估误差

在某些情况下(比如表或者索引的统计信息陈旧或者失真),优化器的成本评估可能存在误差。举例说明,以下是某条慢SQL的执行计划片段,扫描二级索引XVD_BHGC_SQJYRQ_LOCAL_INDEX后,执行回表操作。可以看到优化器评估返回数据行数1,798,508行,实际返回了47,948,939行,执行耗时420秒。
DM_221213_04.PNG

我们知道另外一个索引XVD_BHGC_DQJYRQ_LOCAL_INDEX具有更好的数据过滤性,从实际执行来看,返回行数只有2218行,执行耗时约40秒。
DM_221213_05.PNG
当我们期望某条SQL选择特定的索引时,可以通过Index Hint的方式实现,或者临时把不优的索引置为“不可见”状态。

寻根究源的话,我们抓取达梦10053 event来分析,不优索引的评估COST只有3464。
DM_221213_06.PNG

正确索引路径的COST评估是80683,被丢弃。更深层面的细节,限于篇幅不再赘述。
DM_221213_07.PNG

需要注意的是,不恰当的数据库对象统计信息采样率,可能会造成优化器评估COST的计算误差。另外,如果数据库服务器升级配置的话,比如从16C64G的虚机升级到128C1TB的物理机,请务必安排一轮关键业务场景的性能测试;优化器对个别SQL的成本评估在服务器升配后可能存在误差。

4.3 Hint干预

当优化器评估出“次优”或者“不优”的执行计划时,有经验的DBA可以根据SQL的关联关系、数据访问路径等,通过HINT来干预优化器,生成“较优”甚至“最优”的执行计划。指定HINT后,可以先通过EXPLAIN SQL方式快速查看HINT是否生效。若生效,再实际执行并收集trace和ET进一步分析。
举例说明,某条SQL在优化前执行了约5小时。按照前面的优化思路,从ET可以看出97.41%的时间消耗在第97步。
DM_221213_08.PNG
第97步的SLCT2对应于第102步的SPL2操作。
DM_221213_09.PNG

为了优化关键部分,我们使用/*+ ENABLE_RQ_TO_NONREF_SPL(3) */ hint,它提供了一种行级处理方式以实现去除相关子查询的相关性,简化了执行计划,不需要生成临时结果集。新的ET如下:
DM_221213_11.PNG

再加上该SQL其他部分的优化,比如优化OR表达式的/*+ OPTIMIZER_OR_NBEXP(2) */ hint,最终执行时间优化到了3秒钟。可见,对查询优化参数的深刻理解和使用得当,可以极大的提升SQL的性能。

还有很多其他有用的hint,比如使用ORDER改变表的关联顺序、使用Index尝试走某一个索引以改变数据访问方式、使用USE_HASH和USE_NL改变表的关联方式等,更多信息请参考达梦官方DBA手册。

引申:达梦提供了一种特殊的INJECT_HINT。它类似于Oracle SQL Profile功能,可以在不修改SQL语句的情况下,通过SF_INJECT_HINT为SQL增加HINT。

4.4逻辑等价改写

当常规优化手段效果不明显,且关键点集中于一处时,对关键点进行必要的逻辑等价改写也是一条可行的道路。需要优化者有一定的SQL开发能力。
举例说明,某SQL优化前执行时间约400秒。ET片段如下:
DM_221213_12.PNG

执行计划第15步:
DM_221213_13.PNG

根据上述执行计划片段,可以定位到SQL片段中的自定义函数fun_getdffryy,该函数被不断的解析、调用了8574次。
DM_221213_15.PNG

为了消除自有函数的解析成本,我们做了等价改写,把函数语义放在了CASE WHEN中执行,SQL语句显得比较“臃肿”,但是效率却提升了十倍。漂亮简洁的SQL代码是一个良好的开发习惯,但和性能是不能划等号的,需要据实分析。
DM_221213_10.PNG

需要注意的是,逻辑改写SQL语句后,必须要做业务的功能测试,确保改写是等价的。

5.小结

性能优化是一个非常庞杂的话题,涉及的内容和技术层面很广泛。单就SQL优化来说,掌握SQL的开发,写出高效率的SQL是最基本的。此外,还要熟练掌握执行计划的生成原理、表间的各种关联方式、数据的访问路径、各种表达式的优化;以及ET的使用、查询优化参数的深刻理解,甚至数据库的底层原理、组件间的交互等。SQL优化的技术手段绝不仅仅限于本文中的几种,从硬件资源(CPU和内存)、存储I/O、网络等层面优化也能达到很好的效果,有时从业务层面优化的话效率往往更高。篇幅所限,本文只是管中窥豹。囿于作者研究水平有限,如有描述错误之处还请斧正。
SQL优化是一门在多因素间平衡的艺术。SQL性能优化,如庖丁解牛,运用之妙、存乎一心。共勉之!

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服