共计 2700 个字符,预计需要花费 7 分钟才能阅读完成。
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 流程回填数据而后写入零碎表实现执行流程,如果不需要的话,则在执行算子的时候就将数据写入到零碎表中实现执行流程。