全部产品
云市场

最佳实践

更新时间:2019-09-18 15:52:24

新的 DDL 任务引擎启用时,当 DDL 执行失败或者被意外中断后,对应的 DDL 任务会处于 PENDING 待处理的状态,这时若不对 PENDING 状态任务进行处理,而是继续执行同一个对象(比如表)上的其它 DDL 时,后续的 DDL 会被禁止执行并报错,此时必须对该 PENDING 状态进行合适的任务处理,才能解除 PENDING 状态,恢复正常访问。

由于 DDL 执行的场景可能非常复杂,无法枚举所有情况加以说明,因此以最佳实践的形式,总结了对 PENDING 任务进行处理的常用原则:

  1. 通过 SHOW [FULL] DDL 语句查看 DDL 任务的信息和失败原因( REMARK 字段会记录异常信息)

  2. 常用的处理方式(供参考,请根据实际情况选择最适合的方式):

    • 分析失败原因,修复或排除导致失败的因素(例如:因数据问题导致的,请订正数据,比如去重;因其它约束导致的,请确认是否能够去掉约束,等等),完成后,使用 RECOVER DDL 恢复该 PENDING 任务;

    • 如果导致失败的因素无法解除,并且 DDL 因失败而并没有能够真正执行,可以使用 REMOVE DDL 删除任务(务必确认 DDL 没有真正执行才可删除,否则可能造成不一致状态),删除后恢复可访问状态;

    • 如果只是想简单直接地删除掉 DDL 任务失败的表(比如表中无数据,可以直接删除重建),可以使用 REMOVE DDL删除任务,然后再执行 DROP TABLE IF EXISTS 删除表(务必确认表中无数据,或者数据可以丢弃,并且 DROP TABLE 一定要指定 IF EXISTS 语法,确保强制删除)

示例:下面以一个简单的示例,说明对于执行失败后处于 PENDING 状态的 DDL 任务进行处理的过程

建表时没有指定主键,并且插入了带有重复值的数据行(id=1有两行数据):

  1. mysql> create table test_pending (id int not null, age int) dbpartition by hash(id);
  2. Query OK, 0 rows affected (0.33 sec)
  3. mysql> insert into test_pending values(1,10),(1,20),(2,20),(3,30);
  4. Query OK, 4 rows affected (0.10 sec)
  5. mysql> select * from test_pending order by id;
  6. +------+------+
  7. | id | age |
  8. +------+------+
  9. | 1 | 10 |
  10. | 1 | 20 |
  11. | 2 | 20 |
  12. | 3 | 30 |
  13. +------+------+
  14. 4 rows in set (0.10 sec)

然后想为 id 加上主键约束(可能经历了一段时间后,或者实际的字段和数据比较复杂,忽视了表中有重复数据的情况),由于表中已有数据违反了唯一性约束,因此 DDL 执行失败:

  1. mysql> alter table test_pending add primary key (id);
  2. ERROR 4636 (HY000): [f5be83373466000][10.81.69.55:3306][ddltest]ERR-CODE: [TDDL-4636][ERR_DDL_JOB_ERROR] Not all physical operations have been done successfully: expected 9,
  3. but done 8. Caused by: 1062:DDLTEST_1562056402230OYMK_7WW7_0001:Duplicate entry '1' for key 'PRIMARY' on `test_pending`;.

通过 SHOW FULL DDL 语句查看任务状态和失败原因,能得知有一个物理表中的数据因为重复值导致了物理 DDL 执行失败:

  1. mysql> show full ddl\G
  2. *************************** 1. row ***************************
  3. JOB_ID: 1106733441212637184
  4. PARENT_JOB_ID: 0
  5. SERVER: 1:102:10.81.69.55
  6. OBJECT_SCHEMA: ddltest
  7. OBJECT_NAME: test_pending
  8. NEW_OBJECT_NAME:
  9. JOB_TYPE: ALTER_TABLE
  10. PHASE: EXECUTE
  11. STATE: PENDING
  12. PROGRESS: 77%
  13. START_TIME: 2019-09-06 17:17:55.002
  14. END_TIME: 2019-09-06 17:17:55.273
  15. ELAPSED_TIME(MS): 271
  16. DDL_STMT: alter table test_pending add primary key (id)
  17. REMARK: ERR-CODE: [TDDL-4636][ERR_DDL_JOB_ERROR] Not all physical operations have been done successfully: expected 9, but done 8. Caused by: 1062:DDLTEST_1562056402
  18. 230OYMK_7WW7_0001:Duplicate entry '1' for key 'PRIMARY' on `test_pending`;.

REMARK 字段中的详细信息解释如下:

  • Not all physical operations have been done successfully: expected 9, but done 8.

    • 该逻辑表的 DDL 涉及到9个物理 DDL 的执行,完成了8个,有1个失败了,这个失败的物理 DDL 导致整个逻辑 DDL 失败,任务被置于 PENDING 状态(STATE)
  • Caused by: 1062:DDLTEST_1562056402 230OYMK_7WW7_0001:Duplicate entry '1' for key 'PRIMARY' on `test_pending`;

    • 失败的根源:位于 DDLTEST_1562056402 230OYMK_7WW7_0001 物理库的 test_pending 物理表中有 id 字段的重复数据1,导致无法添加主键约束

此时逻辑表处于不一致的状态:

  1. mysql> check table test_pending;
  2. +----------------------------------------+-------+----------+-------------------------------------------------------------------------------------------------------------+
  3. | TABLE | OP | MSG_TYPE | MSG_TEXT |
  4. +----------------------------------------+-------+----------+-------------------------------------------------------------------------------------------------------------+
  5. | ddltest_1562056402230oymk.test_pending | check | Error | Table 'DDLTEST_1562056402230OYMK_7WW7_0001.test_pending' find incorrect columns 'id', please recreate table |
  6. +----------------------------------------+-------+----------+-------------------------------------------------------------------------------------------------------------+
  7. 1 row in set (0.04 sec)

执行其它 DDL 也会被禁止,收到相应的错误:

  1. mysql> drop table test_pending;
  2. ERROR 4644 (HY000): [f5beae39d466000][10.81.69.55:3306][ddltest]ERR-CODE: [TDDL-4644][ERR_PENDING_DDL_JOB_EXISTS] Another DDL job '1106733441212637184' with operation 'ALTER_
  3. TABLE' is pending on ddltest.test_pending in ddltest. Please use SHOW DDL to check it, and then recover or rollback it using RECOVER DDL or ROLLBACK DDL, or just remove it us
  4. ing REMOVE DDL if you confirm that the pending job can be discarded.

接下来,根据前面所述的常用原则,有以下几种选择进行继续处理(分别展示它们的效果):

选择1:去重(删除重复的数据)后,恢复 DDL 任务,继续完成添加主键约束的操作

删除重复数据(根据业务需要,仅保留一条数据),删除数据操作可以通过 DRDS 执行,也可以根据报错信息,直接连接到 DRDS 后端的 RDS 物理库中操作:

  1. mysql> delete from test_pending where id=1 and age=20;
  2. Query OK, 1 row affected (0.07 sec)
  3. mysql> select * from test_pending order by id;
  4. +------+------+
  5. | id | age |
  6. +------+------+
  7. | 1 | 10 |
  8. | 2 | 20 |
  9. | 3 | 30 |
  10. +------+------+
  11. 3 rows in set (0.02 sec)

确认表中已经没有重复数据后,恢复之前 PENDING 的 DDL 任务的执行(恢复成功,完成的任务被自动清理,主键添加成功):

  1. mysql> recover ddl 1106733441212637184;
  2. Query OK, 0 rows affected (1.28 sec)
  3. mysql> show full ddl\G
  4. Empty set (0.00 sec)
  5. mysql> show create table test_pending\G
  6. *************************** 1. row ***************************
  7. Table: test_pending
  8. Create Table: CREATE TABLE `test_pending` (
  9. `id` int(11) NOT NULL,
  10. `age` int(11) DEFAULT NULL,
  11. PRIMARY KEY (`id`),
  12. KEY `auto_shard_key_id` (`id`) USING BTREE
  13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 dbpartition by hash(`id`)
  14. 1 row in set (0.02 sec)
  15. mysql> check table test_pending;
  16. +----------------------------------------+-------+----------+----------+
  17. | TABLE | OP | MSG_TYPE | MSG_TEXT |
  18. +----------------------------------------+-------+----------+----------+
  19. | ddltest_1562056402230oymk.test_pending | check | status | OK |
  20. +----------------------------------------+-------+----------+----------+
  21. 1 row in set (0.10 sec)

选择2:直接删除任务,然后删除表(测试数据可以丢弃),后续再根据需要重新创建该表

  1. mysql> remove ddl 1106733441212637184;
  2. Query OK, 1 row affected (0.02 sec)
  3. mysql> drop table if exists test_pending;
  4. Query OK, 0 rows affected (0.44 sec)
  5. mysql> show tables like 'test_pending';
  6. Empty set (0.01 sec)