开发者社区> 问答> 正文

如何处理 DDL 异常

DDL 原理简介

DRDS 的 DDL 指令会在所有分表上执行对应的 DDL 操作。失败的情况可以分为两类:

DDL在分库执行失败。DDL 在任意分库执行出错都可能导致各分表结构不一致。
执行长时间无响应。在对大表执行 DDL 操作时,有可能由于分库的执行时间过长导致 DDL 长时间无响应。
分库执行报错的原因多种多样,如建表时表已存在、加列时列已存在等各类冲突、磁盘空间不足等。

长时间无响应一般是由分库的执行时间过长导致的。以 MySQL(RDS 原理与之类似) 为例,DDL 的耗时大部分取决于该操作是 In-Place(直接在源表修改)还是 Copy Table(拷贝表数据)。In-Place 只需要修改元数据就可以了,而 Copy Table 需要重建整张表数据,此外还涉及日志和 buffer 操作。

各类操作与这两项因素的关系详见 MySQL 官方文档 Summary of Online Status for DDL Operations。

判断 DDL 操作是 In-place 还是 Copy Table 操作,可以查看操作结束后 “rows affected” 这一项的返回值。

示例:

改变某列的默认值(非常快,完全不会影响表数据):

Query OK, 0 rows affected (0.07 sec)

增加一个索引(需要花些时间 ,但是 0 rows affected 说明表数据没有被复制):

Query OK, 0 rows affected (21.42 sec)

改变某列的数据类型(耗费大量时间并且需要重建表中的所有数据行):

Query OK, 1671168 rows affected (1 min 35.54 sec)

因此,执行一个大表 DDL 操作前,可以先通过以下步骤判定这是一个快速或慢速操作:

复制表结构生成一张克隆表。
插入一些数据。
在克隆表上执行这个 DDL 操作。
检查操作完成后 “rows affected” 值是否是0。一个非0的值意味着该操作需要重建整张表,这时可能需要考虑在流量低谷去执行该操作。
失败处理

DRDS DDL 操作会将所有 SQL 分发到所有分库上并行执行。任一分库上执行失败不会影响其他分库。另外,DRDS 还提供了 CHECK TABLE 指令来检测分表结构的一致性。因此,失败的 DDL 操作可以重新执行,已经执行成功的分库上失败报错并不会影响其他分库。只要保证最终所有分表结构一致即可。

DDL 失败处理步骤

使用 CHECK TABLE 指令检查表结构。如果返回结果只有一行且为状态正常则可认为表状态一致。此时进行步骤2,否则进行步骤3。
使用 SHOW CREATE TABLE 指令检查表结构。如果显示的表结构符合 DDL 执行后的预期则可认为 DDL 执行成功,否则继续进行步骤3。
使用 SHOW PROCESSLIST 指令观察所有当前执行的 SQL 状态。如有仍在执行的 DDL 操作,请等候其执行完成后再进行步骤1、2,检查表结构是否符合预期,否则进行步骤4。
在 DRDS 上重新执行 DDL 操作。如果出现 Lock conflict 的报错请进行步骤5,否则进行步骤3。
使用 RELEASE DBLOCK 指令释放 DDL 操作锁,然后进行步骤4。
详细操作如下:

检查表结构一致性

使用 CHECK TABLE 指令检查表结构,当返回结果只有一行且显示状态 OK 时,表明表结构一致。

注意:如果在 DMS 上执行 CHECK TABLE 没有返回结果,请在命令行下重试。

mysql> check table `xxxx`;
+----------------------------+-------+----------+----------+
| TABLE                      | OP    | MSG_TYPE | MSG_TEXT |
+----------------------------+-------+----------+----------+
| TDDL5_APP.xxxx             | check | status   | OK       |
+----------------------------+-------+----------+----------+
1 row in set (0.05 sec)
检查表结构

使用 SHOW CREATE TABLE 指令检查表结构,如果表结构一致且表结构无误时,可认为 DDL 已执行成功。

mysql> show create table `xxxx`;
+---------+------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                     |
+---------+------------------------------------------------------------------------------------------------------------------+
|  xxxx   | CREATE TABLE `xxxx` (
`id` int(11) NOT NULL DEFAULT '0',
`NAME` varchar(1024) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 dbpartition by hash(`id`) tbpartition by hash(`id`) tbpartitions 3                      |
+---------+------------------------------------------------------------------------------------------------------------------+
1 row in set (0.05 sec)
观察当前正在执行的 SQL 语句

有些 DDL 执行速度过慢,发现 DDL 长时间无响应后,可执行 SHOW PROCESSLIST 指令观察所有当前执行的 SQL 状态。

mysql> SHOW PROCESSLIST WHERE COMMAND != 'Sleep';
+---------------+-----------+--------------------+-------------+---------+-----------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+-----------+---------------+-----------+
| ID            | USER      | DB                 | COMMAND     | TIME    | STATE                                                                 | INFO                                                                                                 | ROWS_SENT | ROWS_EXAMINED | ROWS_READ |
+---------------+-----------+--------------------+-------------+---------+-----------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+-----------+---------------+-----------+
| 0-0-352724126 | ifisibhk0 | test_123_wvvp_0000 | Query       |      15 | Sending data                                                          | /*DRDS /42.120.74.88/ac47e5a72801000/ */select `t_item`.`detail_url`,SUM(`t_item`.`price`) from `t_i |      NULL |          NULL |      NULL |
| 0-0-352864311 | cowxhthg0 | NULL               | Binlog Dump |      13 | Master has sent all binlog to slave; waiting for binlog to be updated | NULL                                                                                                 |      NULL |          NULL |      NULL |
| 0-0-402714566 | ifisibhk0 | test_123_wvvp_0005 | Query       |      14 | Sending data                                                          | /*DRDS /42.120.74.88/ac47e5a72801000/ */select `t_item`.`detail_url`,`t_item`.`price` from `t_i      |      NULL |          NULL |      NULL |
| 0-0-402714795 | ifisibhk0 | test_123_wvvp_0005 | Alter       |     114 | Sending data                                                          | /*DRDS /42.120.74.88/ac47e5a72801000/ */ALTER TABLE `Persons` ADD `Birthday` date                    |      NULL |          NULL |      NULL |
......
+---------------+-----------+--------------------+-------------+---------+-----------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+-----------+---------------+-----------+
12 rows in set (0.03 sec)
TIME 列代表该指令已经执行的秒数。发现过慢指令后,例如图中,可使用 KILL ‘0-0-402714795’ 指令来取消慢指令。

注意: DRDS 中一个逻辑 SQL 对应多条分库指令,因此为了停止一个逻辑 DDL 可能需要 Kill 多条指令。从 SHOW PROCESSLIST 结果集的 INFO 列判断该指令归属的逻辑 SQL。

Lock conflict 报错处理

DRDS 执行 DDL 操作先会加库级锁,操作完后再释放掉。KILL DDL 操作很可能会导致该锁没有释放,此时再执行 DDL 会有以下报错:

Lock conflict , maybe last DDL is still running
此时执行 RELEASE DBLOCK 释放该锁即可。指令取消及锁释放后,选择业务低谷甚至停止期间,重新执行该 DDL。

其它问题

DMS 或其它客户端无法显示修改后的表结构

为了兼容某些客户端从系统表(如 COLUMNS 或 TABLES)中获取表结构的功能,DRDS 在用户0分库 RDS 里建立了一个影子库,影子库名与用户的 DRDS 逻辑库名一致。存储了所有用户库里的表结构等信息。

DMS 获取 DRDS 表结构是从影子库系统表中获取的。在处理异常 DDL 过程中,由于种种原因可能会出现用户库表结构正常修改,但是影子库中的表结构却未修改的现象。此时需要用户连接到影子库,在该库中重新对表进行一次 DDL 操作即可。

注意: CHECK TABLE 不会检测影子库表结构与用户库是否一致。

展开
收起
猫饭先生 2017-10-30 13:58:28 6200 0
0 条回答
写回答
取消 提交回答
问答排行榜
最热
最新

相关电子书

更多
面向失败设计 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载