本文介绍了PolarDB-X中DDL的执行逻辑和执行方式。
背景知识
PolarDB-X的架构如下:
PolarDB-X在执行DDL时,几乎各个组件都需要参与,以保证DDL的正确性。
元数据服务节点(GMS)维护了Table/Schema、Statistic等元数据信息;
计算节点(CN)提供了分布式DDL执行、全局索引维护等能力;
存储节点(DN)维护了所有Table/Schema的物理数据,并且具有DDL下推能力(例如ALTER TABLE等DDL操作的下推)。
为了便于说明,本文将用户发送到PolarDB-X实例(由计算节点接收)的DDL称为用户DDL,而在用户DDL执行过程中由计算节点下推发送到存储节点的DDL称为物理 DDL。
DDL执行方式
在PolarDB-X中,DDL按照执行方式主要分为两类,物理执行的DDL和逻辑执行的DDL。
物理执行
物理执行的DDL指的是主要依赖存储节点物理DDL下推能力完成的DDL。物理执行的DDL在执行过程中,计算节点仅维护元数据信息的变化,真正的结构变更由存储节点完成。例如Create Table、Drop Table、Create Local Index以及常用的Alter Table等。
原子性
在PolarDB-X中,一张逻辑表往往对应了多个物理表(分片),且这些分片往往分布在多个存储节点中。在对一张逻辑表做DDL变更时,如果是物理执行方式,那么计算节点会将用户DDL先转换为物理DDL,然后下推到相应的存储节点执行,并且记录各个分片的完成情况,在所有分片完成物理DDL变更后,计算节点再进行元数据的最终变更。
物理执行的DDL在执行过程中是可以并发的,因此在执行过程中,计算节点不仅会记录各个分片的执行情况,还需要保证所有分片变更的原子性,确保所有分片可以一起完成变更,详情请参见DDL原子性。
执行算法
物理执行的DDL在执行过程中是否锁表(允许并发DML)、是否需要重建表(持续时间长短)以及是否仅修改元数据(秒级完成)完全依赖存储节点执行物理DDL的执行算法。
存储节点执行物理DDL的执行算法主要有以下三种:
INSTANT算法,仅需修改数据字典中的元数据,不需要修改或复制存量数据,也不需要重建物理表,物理DDL可以秒级完成;
INPLACE算法,物理表中的数据需要复制和重建,但是复制和重建都在存储引擎内部完成,通常允许并发读写访问,对业务影响较小;
COPY算法,需要将物理表中所有的数据复制到新表中,数据复制期间会阻塞所有写操作(锁表),对业务影响较大。
不同的DDL支持的执行算法会有所不同,详情请参见Online DDL。
逻辑执行
逻辑执行的DDL指的是主要依赖计算节点Online Schema Change能力完成的DDL。逻辑执行通常需要经历创建新的临时表,拷贝存量数据,同步增量数据,并进行元数据切换等步骤,一般执行时间较长,但是全程Online无需锁表,例如:Create Global Index、Drop Global Index、Online Modify Column、Add Primary Key以及分区变更相关DDL等;也存在一些逻辑执行的DDL可以通过直接修改元数据并进行同步方式完成,例如:Rename Table、Rename Global Index等。
逻辑执行的DDL通常拥有以下几个特点:
在线变更,无需锁表,对用户业务影响较小;
通常需要重建表,存在数据拷贝,执行时间相对较长;
保证原子性,不会出现部分分片执行成功,部分分片执行失败的情况。
并行度调整
逻辑执行的DDL通常需要拷贝存量数据,这是一个耗时较长的过程,在计算节点和存储节点不存在CPU、IO等瓶颈的情况下,可以调整存量数据回填的并行度以及数据回填的限速,来提升DDL执行效率。
例如,在创建全局二级索引的过程中,主表的存量数据需要回填到全局二级索引表中,如果计算节点和存储节点的CPU、IO等资源都不存在瓶颈,可以调整数据回填的并行度以及数据回填的限速,来提升数据回填的速度。
-- 设置数据回填的全局并行度,默认值为32,根据经验这个值一般无需调整
set global BACKFILL_PARALLELISM = 32;
-- 物理分片数据回填的并行度,默认值为4,物理分片数据量较大时推荐调整
set global PHYSICAL_TABLE_BACKFILL_PARALLELISM = 8;
-- 设置数据回填的限速,默认值为150000 rows/s
set global GSI_BACKFILL_SPEED_LIMITATION = 250000;
由于不同的表结构、拓扑以及数据量都不尽相同,因此除了上面给出的参数外还有一些其他参数可以调整,详情请参见并行DDL。
如何判断DDL执行方式
在PolarDB-X中,可以通过explain语句来判断DDL的执行方式。如果explain的结果与原本DDL语句类似,则为物理执行的DDL;如果explain的结果中包含类似Create Table语句,则为逻辑执行的DDL。
示例
在PolarDB-X实例中,创建一张逻辑表t1,其结构如下:
+-------+--------------------------------------------------------------------------+
| Table | Create Table |
+-------+--------------------------------------------------------------------------+
| t1 | CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 |
+-------+--------------------------------------------------------------------------+
使用EXPLAIN语句来判断“给逻辑表t1添加一个局部索引”这个DDL操作是逻辑执行还是物理执行,其执行结果如下,可以看到,执行方式为物理执行。
explain alter table t1 add local index idx(b);
+----------------------------------------------------------------------------------+
| EXECUTION PLAN |
+----------------------------------------------------------------------------------+
| ALTER_TABLE( tables="t1", shardCount=16, sql="ALTER TABLE ? ADD INDEX idx (b)" ) |
| EXCLUDE_RESOURCE( wumu.t1, wumu.tg17 ) |
| SHARE_RESOURCE( wumu.t1, wumu.tg17 ) |
+----------------------------------------------------------------------------------+
使用explain语句来判断“将逻辑表t1的分区算法修改为
partition by key(a)
”这个DDL操作是逻辑执行还是物理执行,其执行结果如下,可以看到,执行方式为逻辑执行。
explain alter table t1 partition by key(a);
+----------------------------------------------------------------------------------+
| EXECUTION PLAN |
+----------------------------------------------------------------------------------+
| CREATE_TABLE( tables="t1_msfg", shardCount=16, sql="CREATE TABLE ? (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`_drds_implicit_id_` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`_drds_implicit_id_`),
INDEX `auto_shard_key_a` USING BTREE(`a`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4" ) |
| DROP_TABLE( tables="t1_msfg", shardCount=16, sql="DROP TABLE IF EXISTS ?" ) |
| EXCLUDE_RESOURCE( wumu.t1, wumu.t1_msfg, wumu.tg17 ) |
| SHARE_RESOURCE( wumu.t1, wumu.t1_msfg, wumu.tg17 ) |
+-----------------------------------------------------------------------------------+
如何异步执行DDL
在PolarDB-X中,默认使用同步的方式执行DDL,与MySQL保持一致。在同步执行DDL时,在执行期间如果连接发生中断可能导致DDL执行被暂停,如果预期DDL执行时间过长,可以考虑使用异步的方式执行DDL。
可以通过在要执行的DDL前添加hint的方式来开启异步执行DDL,示例如下:
/*+TDDL:cmd_extra(PURE_ASYNC_DDL_MODE=true)*/ alter table t1 add global index gsi_a(a) partition by key(a);
更多信息,请参见DDL异步执行语法扩展。