唯一索引写入冲突会导致ONLINE DDL报错
现象
最近对业务大表做了归档,有很大的空间未释放,于是想着alter table xxxx engine=innodb进行表空间的收缩,由于此操作是支持online ddl,于是抽了一个业务低峰期搞,发现DDL失败,并报唯一索引冲突。
一、为什么收缩一下表空间会报唯一约束冲突?
于是我在网上查,我查到了一篇文章,链接:http://www.136.la/nginx/show-169305.html
我就备份了表的数据去测试环境复现,cc_coupon_ct_24表结构如下:
mysql> show create table cc_coupon_ct_24\G
*************************** 1. row ***************************
Table: cc_coupon_ct_24
Create Table: CREATE TABLE `cc_coupon_ct_24` (
`coupon_ct_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`biz_type` bigint(11) NOT NULL DEFAULT '0' COMMENT '(0:)',
`merchant_id` bigint(20) unsigned NOT NULL,
`code` varchar(50) NOT NULL,
`wid` bigint(20) unsigned NOT NULL,
`card_template_id` bigint(20) unsigned NOT NULL,
`use_adapt_object_id` bigint(20) DEFAULT NULL COMMENT '&',
`start_date` datetime DEFAULT NULL,
`exp_date` datetime DEFAULT NULL,
`use_time` datetime DEFAULT NULL,
`use_channel` int(11) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`app_channel` tinyint(4) DEFAULT '1',
`business_id` varchar(50) DEFAULT NULL,
`acquire_scene` int(9) DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`acquire_time` datetime DEFAULT NULL,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`card_template_type` int(11) DEFAULT NULL,
`source` tinyint(4) DEFAULT NULL,
`operator` varchar(32) DEFAULT NULL,
`add_to_wechat` tinyint(4) DEFAULT '0',
`name` varchar(20) DEFAULT NULL,
`acquire_store_id` bigint(20) DEFAULT NULL,
`send_channel` int(11) DEFAULT NULL,
`wsc1_ct` int(11) DEFAULT NULL,
`allow_buy` int(11) NOT NULL DEFAULT '0' COMMENT '0- 1-',
`acquire_order_id` varchar(64) DEFAULT NULL COMMENT 'id',
PRIMARY KEY (`coupon_ct_id`),
UNIQUE KEY `uidx_code` (`merchant_id`,`code`) USING BTREE,
KEY `idx_union1` (`card_template_id`,`merchant_id`,`wid`) USING BTREE,
KEY `idx_acquireTime` (`acquire_time`) USING BTREE,
KEY `idx_useTime` (`use_time`) USING BTREE,
KEY `idx_cti_mi_cci` (`card_template_id`,`merchant_id`,`coupon_ct_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=22746161 DEFAULT CHARSET=utf8mb4 COMMENT='-ct'
session 1:针对些表发起ONLINE DDL
alter table cc_coupon_ct_24 engine=innodb;
session 2:执行插入冲突的数据
mysql> insert into cc_coupon_ct_24 values (null,0,42,921110281014034154,272236326,32514,NULL,now(),now(),NULL,NULL,1,1,'',222,now(),now(),now(),0,3,'',0,'wewwe',211,null,1,0,'');
ERROR 1062 (23000): Duplicate entry '42-921110281014034154' for key 'uidx_code'
在DDL快要执行完成的时候,session 1:
mysql> alter table cc_coupon_ct_24 engine=innodb;
ERROR 1062 (23000): Duplicate entry '42-921110281014034154' for key 'uidx_code'
2.那主键冲突会不会影响ONLINE DDL?
session 1:发起ONLINE DDL
alter table cc_coupon_ct_24 engine=innodb;
session 2:插入主键冲突的数据
mysql> insert into cc_coupon_ct_24 values (13274941,0,43,921110281014034154,272236326,32514,NULL,now(),now(),NULL,NULL,1,1,'',222,now(),now(),now(),0,3,'',0,'wewwe',211,null,1,0,'');
ERROR 1062 (23000): Duplicate entry '13274941' for key 'PRIMARY'
session 1:最终DDL成功
mysql> alter table cc_coupon_ct_24 engine=innodb;
Query OK, 0 rows affected (4 min 32.98 sec)
Records: 0 Duplicates: 0 Warnings: 0
总结
提示:引用链接文章中的总结:
row_log_table_apply_insert_low //(insert行为)row log增量DML日志过程
|-row_ins_clust_index_entry_low //对于 聚簇索引 的数据插入
|-row_ins_duplicate_error_in_clust_online //先检测主键是否冲突 冲突的话 err != DB_SUCCESS
|-row_log_table_insert //err == DB_SUCCESS & onlineDDL的情况下 再插入row log增量DML日志 所以在onlineDDL时 主键冲突不会影响
|-row_ins_sec_index_entry_low //对于 二级索引 的数据插入
|-row_log_online_op_try //先插入row log增量DML日志
|-row_ins_scan_sec_index_for_duplicate //再进行唯一性约束检查 会导致以上onlineDDL时 唯一索引重复值报错问题的出现。