数据库版本5.7.x 在执行ALTER TABLE ADD COLUMN语句时,报了Duplicate entry...的错误;
两张表大小分别为:千万行级别
Concurrent DML makes changes to the table that are allowed with the original table definition,but not with the new one.The operation only fails at the very end,when MySQL tries to apply all the changes from concurrent DML statements.
For example,
you might insert duplicate values into a column while a unique index is being created,or you might insert NULL values into a column while creating a primary key index on that column.
The changes made by the concurrent DML take precedence,and the ALTER TABLE operation is effectively rolled back.
原因:
下方 ' rebuild表中第10步 ' 中应用DML时,在唯一索引上插入了重复的值,导致alter table操作被回滚;
更改语句为:
ALTER TABLE t1 xx....xx,ALGORITHM=inplace, LOCK=SHARED;
官网链接:
https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-failure-conditions.html
rebuild表时,Online DDL的关键流程说明:
1. 持有 MDL_SHARED_UPGRADABLE 锁,检测表时是否存在
2. 升级到 MDL_EXCLUSIVE 锁,禁止读写
3. 更新数据字典对象
4. 分配row_log对象记录增量
5. 生成新表
6. 降级为 MDL_SHARED_UPGRADABLE ,允许对原表进行读写(wait_while_table_is_used)
7. 用DDL事务的上下文,扫描老表中对该事务可见的数据,并用merge排序,最后插入到新表。
8. 在执行期间,原表的读写不阻塞,增量应用到原表中,并且会记录到row_log中
9. 进入commit阶段,升级到 MDL_EXCLUSIVE 锁,禁止读写
10. 在新表中apply row_log里的增量(row_log_apply)
11. 更新innodb的数据字典表
12. 提交DDL事务
13. 重命名新表的ibd文件
(参考:阿里数据库内核月报)

发展历程
MySQL Online DDL这个特性是在MySQL5.6.7开始支持的,更早期版本的MySQL进行DDL操作时,会阻塞DML,
我们常说的online DDL,其实是从DML操作的角度描述的,如果DDL操作不阻塞DML,那么这个DDL就是online的。

算法说明
MySQL 5.7 支持COPY、INPLACE
MySQL 8.0 支持COPY、INPLACE、INSTANT(8.0.12版本引入)
- copy方式:
拷贝方式改表;是指DDL时,会生成(临时)新表,将原表数据逐行拷贝到新表中,在此期间会阻塞DML;
步骤:
1. 创建与原表结构定义一致的临时表;
2. 对原表加锁,不允许执行DML,但允许查询;
3. 在临时表上执行DDL语句;
4. 逐行拷贝原表数据到临时表;
5. 原表与临时表进行RENAME操作,加锁不允许读写直至DDL完成。
- Inplace 方式:
原地改表,无需拷贝全表数据到新表,在操作准备和执行阶段,可能会短时间内持有表上的排他元数据锁,除此外,DDL期间不会阻塞DML;在InnoDB层完成, 不需要使用Redo log等。
步骤:
1. 创建临时的frm文件;
2. 对原表加锁,不允许执行DML,但允许查询;
3. 根据聚集索引的顺序,构造新的索引项,按照顺序插入新索引页;
4. 升级原表上的锁,不允许读写操作;
5. 替换原表的frm文件,完成DDL操作。
- INSTANT 方式:
只需修改数据字典中的元数据,无需拷贝数据也无需重建整表,同样,也无需加排他MDL锁,原表数据也不受影响,整个DDL过程几乎是瞬间完成的,也不会阻塞DML;

并发控制(锁)说明
ALTER TABLELOCK
ALTER TABLE t1 ...., ALGORITHM=...., LOCK=....;
//允许并发查询和DML操作
LOCK=NONE
//允许并发查询,但不允许DML操作
LOCK=SHARED
//允许尽可能多的并发查询或DML操作,没指定LOCK选项默认就为DEFAULT
LOCK=DEFAULT
//不允许并发查询和DML操作
LOCK=EXCLUSIVE
说明:
alter table的变更按照 INSTANT → inplac → copy这个顺序从性能低到性能高的逐个尝试使用;
锁控制按照 NONE → shared → EXCLUSIVE 这个顺序逐步尝试通过加锁阻塞DML加快完成变更操作。

示例语句
//执行速度快,对业务影响小;
ALTER TABLE t1 xx....xx,ALGORITHM=instant;
注意:如不支持则会报错:ERROR 1845 (0A000): ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY/INPLACE.
//执行时间长;不加锁;几乎不影响业务读写,变更不使用额外磁盘空间
ALTER TABLE t1 xx....xx,ALGORITHM=inplace, LOCK=NONE;
//执行时间比LOCK=NONE短,写会受到mdl锁阻塞,变更不使用额外磁盘空间
ALTER TABLE t1 xx....xx,ALGORITHM=inplace, LOCK=SHARED;
//执行时间比LOCK=NONE短,读写会受到mdl锁阻塞,变更不使用额外磁盘空间
ALTER TABLE t1 xx....xx,ALGORITHM=inplace, LOCK=EXCLUSIVE;
//如果不知道如何设置上面的选项,可以设置成 DEFAULT
ALTER TABLE t1 xx....xx,ALGORITHM=inplace, LOCK=DEFAULT;

各版本支持说明
https://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl-operations.html
https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html

注意事项
[ 无论执行的DDL语句是复杂还是简单, 都需要选择在业务低峰期进行操作 ]
- 空间需求
-合并拆分同表的DDL操作
早期不支持Online DDL时通常将同一张表中的多个DDL合并一起执行,以便减少多次rebuild表带来的性能消耗;现在Online DDL特性出现之后,可以通过COPY方式和INPLACE方式来进行分类并合并分组。
其中INPLACE方式又可以根据是否rebuild表来进行分组合并,尽量减少DDL对系统的CPU、I/O资源的影响。
-对于一些大表进行Online DDL并需要重建表的操作
现在还没有机制可以做到暂停Online DDL的操作或者限制Online DDL对服务器 CPU、I/O资源的使用;如果Online DDL执行失败,则回滚有可能会是一项昂贵的操作;
执行时间过长的Online DDL可能会导致主从复制的延迟,因为主 库在执行DDL时可能允许DML并发操作,而在从库只能在执行完DDL语句之后再进行应用DML语句操作。