通过前几节的介绍,了解了一条 SQL 请求在 Parser 模块主要关注的是语法树逻辑结构,在 Resolver 模块主要关注的是符合语义要求的树形的结构。在 OceanBase 中,数据结构在 Resolver 层实际上是一个 Statement,这也是 OceanBase 后续进行 Transformer 和 optimizer 改写的基础。
不过在 Transformer 和 optimizer 层,重点需要关注的是火山模型的结构,火山模型详细介绍请参见 4.4 Executor 模块。这里介绍一个示例来帮助大家理解,示例中 SQL 的语义是:挑选出某场电影的名字和卖出的票数,并且它的票数需要大于 50 张。这里假设在七点钟,只有一场电影在播放,并且卖出的票数超过了 100 张。即当执行这条 SQL 的时候,会查出这场电影和卖出的票数。
SELECT movie.name, play.ticket
FROM movie
JOIN play
ON movie.time = play.time
WHERE play.ticket > 50;
下图所示是这条 SQL 简单的执行计划。首先分别对两张表进行全部字段的遍历,遍历所有字段后做表连接操作,操作的条件为 movie.time=play.time
,即时间一致。连接之后生成一张新表,再去根据条件 play.ticker > 50
去过滤,最后将需要查询的 play.ticker
和 movie.name
给投影出来。
接下来看一下它是如何优化的。
投影操作实际上只需要投影出特定的列,包括在做 Join 条件时也是要需要一个时间的字段,所以第一个优化是减少遍历属性,可以将上层的投影给去掉,然后将 Scan 的操作只遍历特定的属性,两张表都是这样,做完这个优化后,就减少了一个投影操作。
第二个优化是因为最终符合条件的只需要卖出的票数大于 50 张的场次,所以在遍历 play 表的时候,不需要将所有的电影及对应票数都筛选出来,只需要筛选出票数大于 50 张的排片。
经过优化之后它由四层的结构变成了两层,只需遍历出需要的字段,然后做一个 Join 操作就可以了。不仅操作路径变短了,而且表的连接项也变得更少了,相比之前查询计划更优。
大部分成熟的数据库都会实现相关的计划优化模块,如下图所示,在 OceanBase 的数据库中,针对于查询计划也实现了一个比较完整的的优化模型, OceanBase 的优化模型主要包括查询改写、代价模型和统计信息三个模块。各模块的详细内容请参见开发者进阶教程。
关于这两个模块最主要的内容就是前文讲的,大家要懂得如何从一个火山模型的层次调用结构去看是否有更多的优化空间。