物理执行计划是物理运算符构成的一个树状结构,每个物理运算符是一个迭代器。执行引擎可以视为是执行 SQL 物理运算符的虚拟机,处理过程中,数据流自底向上,控制流自顶向下。执行引擎的设计目标是性能第一,凡是可以不在执行阶段做的判断,都在编译优化阶段进行。例如,对于隐式类型转换的类型推导,某个特定算法的物理实现,都会在优化期间固定下来。
调度器把执行计划分为本地、远程、分布式三种作业类型,在对外提供统一接口且不侵入 SQL 执行引擎的同时,根据三种作业类型的特点,充分利用存储层和事务层的特性,实现了各自情况下最合适的调度策略。
- 本地作业
所有要访问的数据都位于本机的查询,就是一个本地作业。调度器对于这样的执行计划,无需多余的调度动作,直接在当前线程运行执行计划。事务也在本地开启。如果是单语句事务,则事务的开启和提交都在本地执行,也不会发生分布式事务。这样的执行路径和传统单机数据库类似。
- 远程作业
如果查询只涉及到一个分区组,但是这个分区组的数据位于其他服务器上,这样的执行计划就是远程作业。调度器把整个执行计划发送到数据所在机器上执行,查询结果流式地返回给调度器,同时流式地返回给客户端。这样的流式转发能够提供较优的相应时间。不仅如此,远程作业对于事务层的意义更大。对于一个远程作业,如果是单语句事务,事务的开启提交等也都在数据所在服务器上执行,这样可以避免事务层的RPC,也不会发生分布式事务。
- 分布式作业
当查询涉及的数据位于多台不同的服务器时,需要作为分布式作业来处理,这种调度模式同时具有并行计算的能力。对于分布式计划,执行时间相对较长,消耗资源也较多。对于这样的查询,我们希望能够在任务这个小粒度上提供容灾能力。每个任务的执行结果并不会立即发送给下游,而是缓存到本机,由调度器驱动下游的任务去拉取自己的输入。这样,当任务需要重试时,上游的数据是可以直接获取到的。同时,对于分布式的计划,需要在调度器所在服务器上开启事务,事务层需要协调多个分区,必要时会产生分布式事务。