作为一个10多年工作经验的dba,日常大量的工作就是数据库优化,虽然优化做了好多年,但SQL优化不是我擅长的,sql优化的难度很大,需要如何读懂执行计划,以及优化从大方向上该如何去做,而SQL改写却又是SQL优化最难的一种工作,也是最实用的的一种技术。闲言少叙,下面我用最简单的例子简要的叙述下SQL改写的重要性。
一、 oceanbase 环境
1.租户信息
2.资源规格
二、测试数据构造
1.下载sql
cd /home/admin
wget https://github.com/nomemory/hr-schema-mysql/blob/master/hr-schema-mysql.sql
2.执行sql
MySQL [oceanbase]> source hr-schema-mysql.sql
三、sql改写
1.子查询非嵌套
子查询非嵌套(Subquery Unnesting):当 where 子查询中有 in、not in、exists、not exists
等,oceanbase优化器会尝试将子查询展开(unnest),从而消除 FILTER,这个过程就叫作子查询非嵌套。
子查询非嵌套的目的就是消除 FILTER
现有如下 SQL 及其执行计划(oceanbase-ce-3.1.3):
执行计划中出现了 SUBPLAN FILTER ,驱动表因此被固定为employees 。假设表employees有千万上亿数据,那么该SQL效率就非常差。
现在将上述SQL改写如下及其执行计划(oceanbase-ce-3.1.3):
对 SQL 进行等价改写之后,消除了 FILTER。为什么要消除 FILTER 呢?因为 FILTER的驱动表是固定的,一旦驱动表被固定,那么执行计划也就被固定了。对于 DBA 来说这并不是好事,因为一旦固定的执行计划本身是错误的(低效的),就会引起性能问题,想要提升性能必须改写 SQL 语句。
下面我们继续对上述 SQL 进行等价改写及其执行计划(oceanbase-ce-3.1.3)。
将 SQL 改写为 in 之后,也消除了 FILTER。
2.视图合并
视图合并(View Merge):当 SQL 语句中有内联视图(in-line view,from 后面的子查询),或者 SQL 语句中有用 create view 创建的视图,oceanbase 会尝试将内联视图/视图拆开,进行等价的改写,这个过程就叫作视图合并。如果没有发生视图合并,在执行计划中,我们可以看到关键字,而且视图/子查询会作为一个整体。如果发生了视图合并,那么视图/子查询就会被拆开,而且执行计划中视图/子查询部分就没有 VIEW 关键字。
现有如下 SQL 及其执行计划(oceanbase-ce-3.1.3):
SQL 语句中有内联视图,但是执行计划中没有 V 关键字,说明发生了视图合并。
视图合并是将代表一个视图的子查询合并到包含该视图的查询中,视图合并后,有助于优化器增加联接顺序的选择、访问路径的选择以及进一步做其他改写操作,从而选择更优的执行计划
现在我们添加 HINT: /+no_rewrite()/ 禁止SQL重写,再看执行计划。
执行计划中有v关键字,这说明执行计划中没有发生视图合并。
结论:有时候oceanbase改写后的执行计划更慢,因为ob优化器认为视图合并合成一个连接查询代价会小一点,当我们用no_rewrite让它不再复用缓存计划之后就找到了合适的执行计划。
3.和商业数据库oracle对比执行计划
同样的sql在oracle数据库里执行并查看执行计划(oracle 19.3.0.0.0)
结论:二者的执行计划基本一致。
总结:
本文简要地介绍了OceanBase 的查询改写,查询改写是查询优化器模块的重点和难点,也是SQL性能调优工作者和DBA都需要掌握的基础知识,本文通过OceanBase改写和hint取消改写进行了实验对比 ,发现oceanbase的优化器和Oracle的CBO优化器基本不相上下,实现了大量的改写规则。
评论

