DDL原子性

PolarDB-X支持多阶段DDL功能。该功能避免了用户DDL在物理执行时,出现部分分片执行成功,部分分片执行失败的情况,有效解决了物理DDL原子性的问题(由于逻辑执行的DDL不存在原子性问题,因此本文主要讨论物理执行的DDL)。

背景信息

PolarDB-X中,一张逻辑表往往对应了多个物理表(分片),且这些分片会分布在多个存储节点中。在对一张逻辑表执行DDL操作时,如果是物理执行,那么这张表的所有物理表都需要执行相关物理DDL操作,由于各个分片物理DDL执行是独立的,因此往往不能保证物理执行的原子性;如果是逻辑执行,计算节点通过新增临时表,导入存量数据,双写同步增量数据,切换表元数据的方式实现,并且切换表元数据的过程是原子的,因此不存在原子性问题。

不支持原子性物理DDL的时候,物理DDL在多个分片上独立执行,并无一致提交的时间点,且计算节点和存储节点各自保存元数据信息,同一时间点内,可能出现计算层与存储层多个分配之间的元数据视图存在差异的情况,导致用户查询报错。

另外,对于ALTER TABLE MODIFY COLUMN此类修改列的schema约束的语句,容易因为分片内已经存在的数据违反约束而导致物理DDL执行失败。当部分分片执行物理DDL失败,而部分分片执行物理DDL成功时,计算层无法通过下发物理DDL实现一致的回滚语义,必须人工介入,用户的业务将有较长时间出现元数据视图不一致状态,存在影响用户业务的隐患。

PolarDB-X支持的多阶段DDL功能,通过在DN上构造多个分片提交物理DDL的barrier,控制物理DDL在commit point之前的回滚和提交行为,保证多分片DDL原子性提交,从而提供一致的DN多分片元信息视图。

注意事项

  • 仅内核版本在5.4.18-17108394及以上的PolarDB-X实例支持多阶段DDL功能。

  • 由于所有参与的物理分片都必须保持连接和线程,不支持单个DN存在500个物理分片以上的较大规模逻辑表。

  • 对于COPY类型的物理DDL,分布式DDL的影响由原有的锁分片变为锁全逻辑表。

  • 物理DDL在commit阶段修改数据字典,此操作为全局互斥操作,在多阶段DDL中将表现为集中到锁表时间点。

  • 对于锁表敏感的用户,推荐使用无锁变更列类型,该功能为逻辑执行,同样可以保证原子性。

适用范围

  • 目前仅适用于ADD/DROP COLUMN、ADD/DROP INDEX以及MODIFY COLUMN操作的物理DDL。

  • 不适用包含物理分区、外键、全文索引的表。

使用方法

  • 检查实例是否支持多阶段DDL,以及多阶段DDL的开启状态:

    show variables like "enable_two_phase_ddl";

    如果查询不到结果,那么说明不支持多阶段DDL;如果返回为true则说明已经开启了多阶段DDL功能;如果返回为false则说明未开启多阶段DDL功能。

    -- 默认开启多阶段 DDL 功能
    
    show variables like "enable_two_phase_ddl";
    +----------------------+-------+
    | Variable_name        | Value |
    +----------------------+-------+
    | enable_two_phase_ddl | ON    |
    +----------------------+-------+
  • 开启多阶段DDL功能:

    set global enable_two_phase_ddl = true;
  • 关闭多阶段DDL功能:

    set global enable_two_phase_ddl = false;

示例

  1. 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
    PARTITION BY KEY(`a`)
    PARTITIONS 16                                                                      |
    +-------+--------------------------------------------------------------------------+
  2. 写入数据到逻辑表t1中。

    insert into t1 values(1,1),(2,2),(3,3),(4,4),(5,999999);
    Query OK, 1 row affected (0.01 sec)
  3. 在严格模式下,执行ALTER TABLE t1 MODIFY COLUMN b tinyint,由于t1表存在一条b列为999999,严格模式下该数据所在的分片预期物理DDL执行失败,默认开启多阶段DDL功能,因此预期所有分片都应该变更失败。

    ALTER TABLE t1 MODIFY COLUMN b tinyint;
    ERROR 4700 (HY000): ERR-CODE: [TDDL-4700][ERR_SERVER] server error by Failed to execute the DDL task. Caused by: ERR-CODE: [TDDL-4636][ERR_DDL_JOB_ERROR] ERR-CODE: [TDDL-4636][ERR_DDL_JOB_ERROR] failed to execute on group(WUMU_P00000_GROUP): /* drds_two_phase_ddl(1717761420461916160)*/ALTER TABLE `t1_KdRs_00004`\n\tMODIFY COLUMN b tinyint , Caused by: Data truncation: Out of range value for column 'b' at row 1. .
  4. 执行SHOW DDL和SHOW DDL RESULT命令,查看当前实例库中的DDL任务和历史执行结果。

    show ddl;
    Empty set (0.01 sec)
    
    show ddl result;
    +---------------------+-------------+-------------+--------------+-------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | JOB_ID              | SCHEMA_NAME | OBJECT_NAME | DDL_TYPE     | RESULT_TYPE | RESULT_CONTENT                                                      |
    +---------------------+-------------+-------------+--------------+-------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 1717761420575162368 | wumu        | t1          | ALTER_TABLE  | ERROR       | Failed to execute the DDL task. Caused by: ERR-CODE: [TDDL-4636][ERR_DDL_JOB_ERROR] ERR-CODE: [TDDL-4636][ERR_DDL_JOB_ERROR] failed to execute on group(WUMU_P00000_GROUP): /* drds_two_phase_ddl(1717761420461916160)*/ALTER TABLE `t1_KdRs_00004`\n\tMODIFY COLUMN b tinyint , Caused by: Data truncation: Out of range value for column 'b' at row 1. .  |
    +---------------------+-------------+-------------+--------------+-------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 rows in set (0.03 sec)
  5. 执行CHECK TABLE命令,检查逻辑表各个分片的结构是否一致,并且与计算层的元数据相同。

    check table t1;
    +---------------------------------+-------+----------+----------+
    | TABLE                           | OP    | MSG_TYPE | MSG_TEXT |
    +---------------------------------+-------+----------+----------+
    | wumu.t1:Topology                | check | status   | OK       |
    | wumu.t1:Columns                 | check | status   | OK       |
    | t1.auto_shard_key_a:Local Index | check | status   | OK       |
    +---------------------------------+-------+----------+----------+
    3 rows in set (0.01 sec)
    
    show create table t1;
    +-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Table | Create Table                                                                                 |
    +-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | t1    | CREATE TABLE `t1` (
            `a` int(11) DEFAULT NULL,
            `b` int(11) DEFAULT NULL,
            KEY `auto_shard_key_a` USING BTREE (`a`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
    PARTITION BY KEY(`a`)
    PARTITIONS 16 |
    +-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.01 sec)