diff --git "a/app/zh/blogs/datarinpoche/2024-03-04-Oracle\345\210\260openGauss\347\232\204SQL\350\257\255\345\217\245\347\272\247\350\260\203\344\274\230.md" "b/app/zh/blogs/datarinpoche/2024-03-04-Oracle\345\210\260openGauss\347\232\204SQL\350\257\255\345\217\245\347\272\247\350\260\203\344\274\230.md" new file mode 100644 index 0000000000000000000000000000000000000000..6c52c9c9ac3fcfd04f47a543184bc82f19facf78 --- /dev/null +++ "b/app/zh/blogs/datarinpoche/2024-03-04-Oracle\345\210\260openGauss\347\232\204SQL\350\257\255\345\217\245\347\272\247\350\260\203\344\274\230.md" @@ -0,0 +1,209 @@ +--- +title: 'Oracle到openGauss的SQL语句级调优' +date: '2024-03-24' +category: 'blog' +tags: ['SQL调优'] +archives: '2024-03' +author: 'DataRinpoche' +summary: 'Oracle到openGauss的SQL语句级调优' +--- + + + +针对Oracle到openGauss迁移后的性能差异,由于SQL优化引擎的变动,我们往往需要进行语句级的SQL调优,以下我个人在实际项目中总结的一些常见的优化点。 + +## 获取执行计划 + +SQL调优的主要途径为根据执行计划进行SQL调优,在openGauss中可以通过explain sql获取指定SQL的执行计划 + +短期内可执行完成的SQL,获取实际执行计划,命令如下 + +``` +explain analyze sql_text; +``` + +短期内无法执行完成的SQL,获取估算执行计划,命令如下 + +``` +explain sql_text; +``` + +## 执行计划可视化解析工具 + +pev2是一款开源的优秀的Postgresql 执行计划可视化工具,我们同样可以用来进行openGauss 执行计划的呈现,下载如下html文件 + +https://www.github.com/dalibo/pev2/releases/latest/download/index.html + +使用浏览器打开该文件,将上部获取到的执行计划文本粘贴至Plan输入框内,点击Submit,可直接生成可视化执行计划树 + + + + + + + + + +对于慢SQL,我们首要关注节点耗时,点击左侧time按钮,各节点耗时将通过条形图显示,我们选取耗时最高的sql 节点,进行分析及优化 + +如上样例执行计划中,耗时最高的SQL 节点如下 + + + + + +可以发现该节点下,存在大量seq scan即顺序全表扫描,如果为实际SQL,我们可以在相应表上添加索引以提升性能 + +## 常见SQL优化点 + +### 更新表统计信息 + +openGauss中执行计划生成依赖于表统计信息,表统计信息越及时越准确,生成的执行计划往往越精确越优。但由于表数据的更新规模与比率问题,运行时间较长的数据库中,表统计信息往往会出现偏差,导致SQL优化器错误估算代价,生成较慢的执行计划,因此,我们需要定期手工进行表统计信息的更新,执行如下命令 + +``` +analyze table_name; +``` + +DBA可创建定时任务或脚本,在系统空闲期间进行重要表或全量表的统计信息刷新动作 + + + +### 改写SQL中的包/自定义函数引用 + +openGauss具备出色的oracle/mysql兼容性,目前支持自定义函数/存储过程/包等对象的使用,因而兼容在sql中引用自定义函数/包。但受限于当前执行计划优化器的解析,如果sql中引用的包/函数存在表查询操作等,执行计划无法将包/函数内的sql与外层SQL优化,导致性能极度劣化,因此,我们需要将包/函数内的SQL提取,与外层SQL结合,改写为直查的SQL以提升性能。 + +### 添加缺失的索引 + +重点关注执行计划中join条件(对应sql 中on子句)与filter条件(对应sql中where)子句,如果出现seq scan即全表扫描,可以考虑在相应的表字段添加合适的索引,以提升性能 + +### 类型不一致导致索引失效 + +在多表连接查询中,连接条件在不同表中有可能会存在数据类型不一致的情况,如A表为varchar,b表为int,即使两个字段都存在索引,也会因为类型转换导致索引失效,我们可以考虑在驱动表字段添加类型转换使索引生效 + +如样例 t1表 c为int,t2表c为varchar,两个字段都具有索引,在如下sql中也会失效 + +``` +select t1.a,t2.b +from t1 +join t2 on t2.c=t1.c +where t1.d='condition' +``` + +改写为 + +``` +select t1.a,t2.b +from t1 +join t2 on t2.c=t1.c::varchar +where t1.d='condition' +``` + +### 手工指定hint + +受限于执行计划生成的智能度,部分复杂的多表关联SQL中,即使表统计信息准确、sql语法没有问题,生成的执行计划有可能不走索引,或没有进行合理的表查找顺序,针对该类情况,我们可以通过sql hint的方式,强制执行计划走最优计划 ,语法如下 + +``` +/*+ */ +``` + +详细的hint语法,请参照openGauss官方文档 + +https://docs-opengauss.osinfra.cn/zh/docs/5.0.0/docs/PerformanceTuningGuide/Join%E9%A1%BA%E5%BA%8F%E7%9A%84Hint.html + +### 子查询改写为join + +针对结果集返回条数较多的SQL查询,如果select子句中存在子查询,且子查询存在索引,可以考虑将子查询改写为join,避免逐行表扫描调用,尤其是子查询存在聚合查询如count/max/min等操作 + +样例SQL + +``` +select t1.a, +(select max(t2.a) from t2 where t2.b=t1.b) +from t1 where t1.c='condition' +``` + +可以考虑改写为 + +``` +select t1.a, +t3.max_value +from t1 where t1.c='condition' +join (select max(t2.a) max_vlaue ,t2.b from t2 group by t2.b) t3 +on t3.b=t1.b +``` + +### in/not in子查询改写为exists/not exists + +where条件通过in/not in (select a from b)子查询进行条件过滤,执行计划往往会生成半嵌套查询,性能较差,我们可以等价改写为exists/not exists或更进一步改写为join进行优化 + +### or条件改写为union/union all + +复杂sql查询中,有可能会在where中出现or条件,如and ((xxxx and exists) or (xxxx and xxxx)),该种句式往往会存在性能问题,针对该类问题,我们可以考虑改写为union 或union all + +即 + +``` +select * from t1 where t1.a='condition' and ((xxxx and exists) or (xxxx and xxxx)) +``` + +改写为 + +``` +select * from t1 where t1.a='condition' and (xxxx and exists) +union /union all --根据是否会存在重复选取 +select * from t1 where t1.a='condition' and (xxxx and xxxx) + +``` + +### 避免视图的深层嵌套 + +在当前版本openGauss下,如果join表中存在视图的多重嵌套,或join 条件存在uinon等情况,SQL优化器无力将外层的过滤条件或关联条件下沉,会导致视图内查询的全表扫描,返回过大的中间结果集,导致CPU占用飙升,查询性能较慢,我们需要提取视图的定义SQL,进行等价改写 + +如存在视图VA,定义为 + +``` +select vb.a,vb.b +from vb +join tm +on vb.c=tm.c +``` + +视图VB定义如下 + +``` +select tx.a,tx.b,ty.a +from tx +left join ty +on tx.c=ty.c +``` + +存在SQL如下,该sql往往会存在性能问题 + +```sql +select t1.*,va.* +from t1 +join va +on va.a=t1.a +where t1.b='condition' + +``` + +改写如下 + +```sql +select t1.*,va.* +from t1 +join (select vb.a,vb.b +from (select tx.a,tx.b,ty.a +from tx +left join ty +on tx.c=ty.c ) vb +join tm +on vb.c=tm.c) va +on va.a=t1.a +where t1.b='condition' +``` + +### 他山之石可以攻玉 + +对于原oracle数据库执行性能正常而openGauss性能劣化的SQL,如果单纯分析openGauss执行计划无法发现性能瓶颈或优化点,我们可以考虑参照oracle的执行计划进行比对,发现openGauss执行计划中的问题,从而进行SQL优化 \ No newline at end of file diff --git a/app/zh/blogs/datarinpoche/explan_node.png b/app/zh/blogs/datarinpoche/explan_node.png new file mode 100644 index 0000000000000000000000000000000000000000..0723d534641ee06650e387a7a2f1f7c5d84c4b6f Binary files /dev/null and b/app/zh/blogs/datarinpoche/explan_node.png differ diff --git a/app/zh/blogs/datarinpoche/explan_tree.png b/app/zh/blogs/datarinpoche/explan_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..fc51ae55a8e7c817cc642329fc839f396ccb9f89 Binary files /dev/null and b/app/zh/blogs/datarinpoche/explan_tree.png differ diff --git a/app/zh/blogs/datarinpoche/input_explan.png b/app/zh/blogs/datarinpoche/input_explan.png new file mode 100644 index 0000000000000000000000000000000000000000..fb2dbbfac5e87d91370ff6d0b7dfa7cb5a7a0818 Binary files /dev/null and b/app/zh/blogs/datarinpoche/input_explan.png differ