SQL 是一种“描述型”语言。与“过程型”语言不同,用户在使用 SQL 时,只描述了“要做什么”,而不是“怎么做”。因此,数据库在接收到 SQL 查询时,必须为其生成一个“执行计划”。OceanBase 的执行计划与其他关系数据库类似,本质上是由物理操作符构成的一棵执行树。物理操作符一般对应一个关系操作,如表扫描、连接、聚合、排序等。执行计划通过将不同的物理操作符按照一定的先后顺序组织在一棵执行树中,最终完成该 SQL 查询。

执行计划形状

执行计划在 OceanBase 中被实现为一棵由操作符组成的“二叉树”(OceanBase 目前暂不支持操作自分支超过两个的“多元操作符”)。当查询涉及到多个关系(Relation)时,优化器会通过依次执行多个 join 操作,将多个关系连接在一起,最终完成执行。执行树从形状上可以分为“左深树”、“右深树”和“多枝树”三种(参见下图),目前 OceanBase 的优化器只支持左深树的计划生成。

image

物理操作符

物理操作符是执行计划的基本单元,多个物理操作符按照“二叉树”的形式组成一个完整的执计划。

常见的物理操作符如下表所示:

类型

物理操作符

表访问

table scan,table get

Join

NLJ, BNLJ,MJ, HJ

排序

sort,top-n sort

聚合

merge group-by,hash group-by

分布式exchange in/out
集合union, except, intersect
其他limit, material, subplan,expression

执行计划缓存

执行计划的生成过程非常复杂,优化器需要综合考虑多种因素,为 SQL 生成“最佳”的执行计划。因此,查询优化的过程本身也是一个比较耗时的过程,当 SQL 本身执行耗时较短时,查询优化所带来的开销也变得不可忽略。一般来说,数据库在这种场景会缓存之前生成的执行计划,以便在下次执行该 SQL 时直接使用,这种策略被称为“optimize once”,即“一次优化”。实际场景中,“一次优化”的策略虽然可以大大降低每次执行时优化的开销,但也会引入一些问题。例如,当同一条 SQL 不同的参数值需要使用不同计划时,“一次优化”的策略就无法处理。针对这种场景,OceanBase 2.0 引入了自适应计划共享(Adaptive Cursor Sharing)功能,能够较好地处理不同参数条件下的计划选择。

OceanBase 内置有计划缓存,会将首次优化后生成的计划缓存在内存中,随后的执行会首先访问计划缓存,查找是否有可用计划,如果有,则直接使用该计划;否则将执行查询优化的过程,相关内容可以参考执行缓存计划一节。