DDL原理简介
PolarDB-X 1.0的DDL指令会在所有分表上执行对应的DDL操作。失败的情况可以分为两类:
- DDL在分库执行失败。DDL在任意分库执行出错都可能导致各分表结构不一致。分库执行报错的原因多种多样,如建表时表已存在、加列时列已存在等各类冲突、磁盘空间不足等。
- 执行长时间无响应。在对大表执行DDL操作时,有可能由于分库的执行时间过长导致DDL长时间无响应。长时间无响应一般是由分库的执行时间过长导致的。以MySQL为例,DDL的耗时大部分取决于该操作是In-Place(直接在源表修改)还是Copy Table(拷贝表数据)。In-Place只需要修改元数据就可以了,而Copy Table需要重建整张表数据,此外还涉及日志和buffer操作。各类操作与这两项因素的关系详见MySQL官方文档Online 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的值意味着该操作需要重建整张表,这时可能需要考虑在业务低峰期执行该操作。
PolarDB-X 1.0 DDL操作会将所有SQL分发到所有分库上并行执行。任一分库上执行失败不会影响其他分库。另外,PolarDB-X 1.0还提供了CHECK TABLE指令来检测分表结构的一致性。因此,失败的DDL操作可以重新执行,已经执行成功的分库上失败报错并不会影响其他分库。只需保证最终所有分表结构一致即可。
DDL失败处理步骤
- 使用CHECK TABLE指令检查表结构。如果返回结果只有一行且为状态正常则可认为表状态一致。此时进行步骤2,否则进行步骤3。
- 使用SHOW CREATE TABLE指令检查表结构。如果显示的表结构符合DDL执行后的预期则可认为DDL执行成功,否则继续进行步骤3。
- 使用SHOW PROCESSLIST指令观察所有当前执行的SQL状态。如有仍在执行的DDL操作,请等候其执行完成后再进行步骤1、2,检查表结构是否符合预期,否则进行步骤4。
- 在PolarDB-X 1.0上重新执行DDL操作。如果出现
Lock conflict
的报错请进行步骤5,否则进行步骤3。 - 使用RELEASE DBLOCK指令释放DDL操作锁,然后进行步骤4。
详细操作如下:
- 检查表结构一致性。使用CHECK TABLE指令检查表结构,示例如下:
mysql> check table `xxxx`;
说明 如果在DMS上执行CHECK TABLE没有返回结果,请在命令行下重试。
若返回结果只有一行且显示状态OK时,表明表结构一致。示例如下:
+----------------------------+-------+----------+----------+ | TABLE | OP | MSG_TYPE | MSG_TEXT | +----------------------------+-------+----------+----------+ | TDDL5_APP.xxxx | check | status | OK | +----------------------------+-------+----------+----------+ 1 row in set (0.05 sec)
- 检查表结构。使用SHOW CREATE TABLE指令检查表结构,示例如下:
mysql> show create table `xxxx`;
- 若表结构一致且表结构无误时,可认为DDL已执行成功,返回结果示例如下:
+---------+------------------------------------------------------------------------------------------------------------------+ | 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列代表该指令已经执行的秒数。发现耗时较长的指令(如示例中ID为
0-0-402714795
的指令)后,您可使用KILL '0-0-402714795'命令来取消该慢指令。
说明PolarDB-X 1.0中一个逻辑SQL对应多条分库指令,因此为了停止一个逻辑DDL可能需要Kill多条指令。您可以从SHOW PROCESSLIST结果集的INFO列判断该指令归属的逻辑SQL。 Lock conflict
报错处理。PolarDB-X 1.0执行DDL操作先会加库级锁,操作完后再释放掉。KILL DDL操作很可能会导致该锁没有释放,此时再执行DDL会出现以下报错:
Lock conflict , maybe last DDL is still running
- 此时执行RELEASE DBLOCK命令释放该锁即可。指令取消及锁释放后,您可以选择在业务低谷或者停止期间,重新执行该 DDL。
常见问题
- Q:为什么在DMS或其它客户端上无法显示修改后的表结构?A:为了兼容某些客户端从系统表(如COLUMNS或TABLES)中获取表结构的功能,PolarDB-X 1.0在您的0分库RDS里建立了一个影子库,影子库名与PolarDB-X 1.0逻辑库名一致,存储了逻辑库里所有的表结构等信息。
DMS会从影子库系统表中获取PolarDB-X 1.0的表结构。在处理异常DDL过程中,由于种种原因可能会出现库表结构正常修改,但是影子库中的表结构却未修改的现象。此时您需连接到影子库,在该库中重新对表进行一次DDL操作即可。
说明 CHECK TABLE不会检测影子库表结构与PolarDB-X 1.0逻辑库是否一致。 - Q:执行DDL语句时出现错误码,如TDDL-4500 ERR_PARSER,该如何解决?A:关于PolarDB-X 1.0返回的常见错误码及解决方法,请参见错误代码。