DDL是数据定义语言,用于定义和治理SQL 数据库中的所有对象的语言,罕用的命令有:create,drop,alter等。通常来说,浪潮云溪数据库DDL语句的流程次要分为四个局部,别离是逻辑打算生成,物理打算生成,打算执行和schemachange。本文次要介绍逻辑打算生成,物理打算生成和打算执行。

- 逻辑打算生成 -

逻辑打算生成次要是生成一个planNode逻辑打算节点,每一条SQL都会有本人的planNode,以create table为例,该SQL生成的planNode中蕴含有CREATETABLE statement信息例如表名,有该表所属数据库的信息,还有该表的列的信息等。planNode生成后将其记录到planner的curplan(curplan有以后打算的属性,蕴含形象语法树,planNode,子查问打算等)里,次要流程如下图所示。

以create table语句为例,构建逻辑打算次要是进行memo的构建以及RBO和CBO优化(memo是用来存储查问打算森林的一种数据结构)。首先会初始化一个优化打算的上下文optPlanningCtx,外面会初始化优化器并记录是否应用memo cache进行memo的缓存复用,而这也是DDL与DML语句之间的区别,DDL语句不会复用memo。

构建memo会调用builder的build()办法,对于不同的DDL语句,会调用不同的办法去构建逻辑打算。Create table语句会调用buildCreateTable,构建outScope,次要是生成语句的表达式expr记录在outScope里,最终记录到memo外面。而对于其余的DDL语句则会调用tryBuildOpaque,而后通过ConstructOpaqueDDL函数构建memo的expr。

Memo构建完当前就是进行优化,通过Optmize()函数进入进行RBO和CBO优化,而DDL语句不会进行优化,这也是与DML语句的区别,DML会将优化完的memo退出缓存。而后就是进行逻辑打算planNode的构建,DDL语句中,create语句会执行buildCreateTable进行ConstructCreateTable构建createTableNode,而drop和alter语句则会执行buildOpaque,通过ConstructOpaque返回所对应的planNode,最初将构建好的planNode封装到planTop外面,并且如果是DDL语句会在planTop的flags外面置planFlagIsDDL。

- 物理打算生成 -

逻辑打算生成后,就会依据逻辑打算生成物理打算,次要的流程如下图所示。

在生成物理打算之前会先判断语句是否须要分布式执行,DDL不会进行分布式执行,会生成一个本地的PlanningCtx(PlanningCtx 蕴含在单个查问的整个布局过程中应用和更新的数据)。如果SQL语句有子句的话,则会调用PlanAndRunSubqueries函数先执行子句。DDL语句会进入到wrapPlan进行物理打算的生成。在wrapPlan中会深度优先遍历planNode树,找到第一个反对DistSQL processor的planNode而后在该planNode上递归DistSQL优化,如果有等效的 DistSQL 处理器调用createPlanForNode生成物理打算。

函数首先判断以后planNode的类型,调用对应的函数为planNode创立物理打算(如indexJoinNode会调用createPlanForIndexJoin),DDL则会递归调用wrapPlan将planNode包起来持续解决(wrapPlan只包装节点自身而不包含孩子节点)。而后,调用shouldPlanTestMetadata函数判断是否须要进行元数据处理,如果须要则增加相干信息)。而后再进行创立planNodeToRowSource(该构造体),如果被包裹的planNode是flow中的第一个node,且语句类型是RowsAffected(返回受影响行的计数的语句)则能够应用fast path,planNode子树若反对DistSQL优化会在被优化后连贯到wrapper。返回wrapper后,为物理打算增加LocalProcessor和LocalProcessorIndexes的元素,LocalProcessors数组蕴含了所有的planNodeToRowSource,即被包裹的planNode,记录物理打算的ResultTypes。

每当增加新的PhysicalPlan时,都须要覆写ResultRouters,咱们将只须要一个result router,因为local processor不是分布式的,确保p.ResultRouters只有一个元素,最初填充打算的endpoints就进入执行流程。

- 执行 -

执行过程的次要流程如下图所示。

StartExec是具体执行planNode的办法,依据构建的物理打算对表描述符进行操作。StartExec会先对commend遍历(以alter table为例,commend是一个表批改操作的切片),在遍历中获取集群版本信息,查看在以后版本中是否反对增加列的类型,若反对则为true,反之报错;接着依照打算中的列定义信息生成列描述符,若增加的列是主键或含有唯一性束缚,还会生成索引描述符,将这些描述符会包装成一个个mutation并增加到表描述符的Mutations字段里;而后依据表、mutation等信息创立一个job,job也是通过零碎表system.jobs进行保护,这个job是用来触发及跟踪schema change执行,接着将含有mutations的表形容通过batch更新到零碎表"descriptor"里。

执行流程中最次要的构造体就是batch,表描述符会存在batch外面,通过writeTableDescToBatch函数,在该函数外面,首先判断这个表如果不是一个新的表,须要将表的version+1,而后会验证表描述符格局是否良好,调用addUncommittedTable函数,这容许事务在绕过表租赁机制的状况下查看本人的批改,最初是将表描述符的KV写入到batch外面。batch构建完当前就执行batchRequest。

在执行流程完结后,如果须要数据回填的话,则进入到online schema change流程回填数据而后写入零碎表实现执行流程,如果不需要的话,则在执行算子的时候就将数据写入到零碎表中实现执行流程。