DDL变更是MySQL运维当中,避免不了日常工作,也是影响比较大的操作。为了较少风险,开源社区提供了gh-ost,pt-online-schema-change这样便利的工具,解决了一些堵塞问题。但还是存在很多不确定的因数。
比如当数据量较大的表需要表更时:

●创建触发器过程中穷住了
●binlog同步发生了延迟,导致ddl无法完成
●执行过程中,内存占满,CPU 100% ,IO等待
最终导致阻塞读写请求(DML操作阻塞),影响系统的可用性。
所以ddl操作,要考虑这些因素,避开高峰期,操作评估,备手段需要准备好。
除了上诉方式,官方的Online DDL也是一种很好的方式。online DDL是在mysql5.6版本后加入的特性,用于支持DDL执行期间DML语句的并行操作,提高数据库的吞吐量。目前迭代到8.0 了解一下都有那些变化。

online DDL
MySQL在线DDL特性提供了即时支持instant ,copy方式,还有原表in-place方式。有些过程中也允许并发DML。

关键点
 就是 ALGORITHM,LOCK 这个指标。
 ···
 ALTER TABLE tbl_name ,
 alter_option: {…},
 ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY}
 LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
 ···


为了避免在执行ALTER TABLE 操作时表可读 或不可写,可以在ALTER TABLE 语句中指定一个子句,如果请求是并发级别不可用,则操作立即停止。

构建方式

● COPY:对原始表的一个副本执行操作,将表数据从原始表逐行复制到新表。不允许并发DML。通过临时表拷贝的方式实现的。新建一个带有新结构的临时表,将原表数据全部拷贝到临 时表,然后Rename,完成创建操作。可能内存,io,cpu,硬盘空间使用上升。

大表查询 mysql out of memory_执行过程


● INPLACE:操作避免复制表数据,在操作的准备和执行阶段,可以简单地使用表上的独占元数据锁。通常,支持并发DML。所谓Inplace,也就是在原表上直接进行,不会拷贝临时表。与copy操作相比,向缓冲池中读取的数据更少,这减少了从内存中清除频繁访问的数据。

大表查询 mysql out of memory_mysql_02


大表查询 mysql out of memory_元数据_03


参数old_alter_table系统变量 要强制ALTER TABLE操作ALGORITHM使用COPY算法。● INSTANT:操作仅修改数据字典中的元数据。在准备和执行期间,表上没有独占元数据锁,并且表数据不受影响,这使得操作是瞬时的。允许并发DML。(MySQL8.0.12开始)

上诉图中

大表查询 mysql out of memory_元数据_04

符号的就是即时操作

备注:

可以理解COPY方式是把数据抽出来,在灌入进去,类似于server层的操作。可以支持所有引擎。

INPLACE方式 是在innodb引擎层完成,比如会多个frm ,ibd方式。底层innodb引擎在协助处理这个过程。

INSTANT就是元数据的更改了。

锁级别:
● LOCK=NONE:允许并发查询和DML(允许读和写) 。
● LOCK=SHARED:允许并发查询,但阻塞DML (允许读)
● LOCK=EXCLUSIVE:阻塞并发查询和DML (独占元数据锁),尽可能短的时间内完成DDL操作。特备是服务器空闲时候,可以使用这个子句

执行过程
Online DDL主要包括3个阶段,prepare阶段,ddl执行阶段,commit阶段。下面将主要介绍ddl执行过程中三个阶段的流程。

1)Prepare阶段:初始化阶段会根据存储引擎、用户指定的操作、用户指定的 ALGORITHM 和 LOCK 计算 DDL 过程中允许的并发量,这个过程中会获取一个 shared metadata lock,用来保护表的结构定义。
● 创建新的临时frm文件(与InnoDB无关)。
● 持有EXCLUSIVE-MDL锁,禁止读写。
● 根据alter类型,确定执行方式(copy,online-rebuild,online-norebuild)。假如是Add Index,则选择online-norebuild即INPLACE方式。
● 更新数据字典的内存对象。
● 分配row_log对象来记录增量(仅rebuild类型需要)。
● 生成新的临时ibd文件(仅rebuild类型需要) 。
● 数据字典上提交事务、释放锁。
注:Row log是一种独占结构,它不是redo log。它以Block的方式管理DML记录的存放,一个Block的大小为由参数innodb_sort_buffer_size控制,默认大小为1M,初始化阶段会申请两个Block。

2)DDL执行阶段:执行期间的 shared metadata lock 保证了不会同时执行其他的 DDL,但 DML 能可以正常执行。
● 降级EXCLUSIVE-MDL锁,允许读写(copy不可写)。
● 扫描old_table的聚集索引每一条记录rec。
● 遍历新表的聚集索引和二级索引,逐一处理。
● 根据rec构造对应的索引项
● 将构造索引项插入sort_buffer块排序。
● 将sort_buffer块更新到新的索引上。
● 记录ddl执行过程中产生的增量(仅rebuild类型需要)
● 重放row_log中的操作到新索引上(no-rebuild数据是在原表上更新的)。
● 重放row_log间产生dml操作append到row_log最后一个Block。

3)Commit阶段:将 shared metadata lock 升级为 exclusive metadata lock,禁止DML,然后删除旧的表定义,提交新的表定义。
● 当前Block为row_log最后一个时,禁止读写,升级到EXCLUSIVE-MDL锁。
● 重做row_log中最后一部分增量。
● 更新innodb的数据字典表。
● 提交事务(刷事务的redo日志)。
● 修改统计信息。
● rename临时idb文件,frm文件。
变更完成。

总结
在线DDL变更可能带来的风险,如果操作失败,回滚在线DDL操作的代价可能很高。
● 修改大表结构执行时间往往不能预估,一般时间较长,可能带来的风险有:修改表结构是表级锁,影响DML写入操作。
● 修改大表耗时较长,中途写入失败需要进行回滚,回滚这段时间也是不可写入。
● 修改大表结构容易导致数据库CPU、IO性能损耗,降低MySQL服务性能。
● 修改大表结构容易造成主从延迟加大,影响业务读取。

需要DBA介入评估。
其实最好的方式在从库上修改表结构,将结构变更了的从库设置为主库。