3.在线DDL
DDL 变更¶
DDL(Data Definition Language)是数据库内部的对象进行创建、删除、修改的操作语言,主要包括:加减列、更改列类型、加减索引等类型。
数据库的模式(schema)会随着业务的发展不断变化,如果没有高效的 DDL 功能,每一次变更都有可能影响业务,甚至产生故障。
在数据库的发展历史中,DDL (Data Definition Language) 操作一直是一个重要的挑战。在主流商业数据库的早期版本中,传统的 DDL 操作,往往都需要对数据表进行锁定,以防止在操作过程中发生数据不一致。
这种锁定操作会导致数据库在 DDL 操作期间无法提供服务,对于需要 7*24 运行的大型现代应用来说,这是难以接受的。
为了解决这个问题,ORACLE 自 9i 引入 Online Table Redefinition
,MySQL 自 5.6 引入 OnlineDDL
,都旨在显著减少(或消除)更改数据库对象所需的应用程序停机时间(该特性以下统称 OnlineDDL)。
MySQL 在 8.0 以前就已经支持 Online DDL,在执行时能够不阻塞其它 DML(Insert/Update/Delete)操作,但许多重要的 DDL 操作,如加减列等,仍旧需要等待很长时间(根据数据量的大小)才会生效。
DDL 变更是应用版本部署过程中的常见操作,在核心业务系统中 DDL 操作不能保证在线完成,是会影响业务 7x24 小时的。
最常见的操作莫过于在线加一个字段或者索引,如果数据量比较大的话,伴随而来的往往是长时间的等待,操作期间对表的访问都会不可用,所以一般只能等到凌晨操作,简直就是梦魇一般的存在。
发展历程¶
在 MySQL 5.5
版本中就加入了 INPLACE DD
L 方式,但是因为实现的问题,依然会阻塞 INSERT
、UPDATE
、DELETE
操作,这也是 MySQL
早期版本长期被吐槽的原因之一。
在 MySQL 5.6
中,官方开始支持更多的 ALTER TABLE
类型操作来避免数据拷贝,同时支持了在线上 DDL 的过程中不阻塞 DML 操作,真正意义上的实现了 Online DDL
。
在 MySQL 8.0
进一步引入了 Instant 算法,这是 DDL 操作的一个重大突破。Instant DDL
允许立即执行某些 DDL 操作,如尾部添加字段、修改字段名、修改表名等等,均无需锁定表或复制数据。
MySQL DDL 的注意事项¶
MySQL
在大表上的 DDL 会带来耗时较久、负载较高、额外空间占用、MDL、主从同步延时等情况。需要特别引起重视。
-
DDL 所需时间 DDL 的执行时间,和很多因素相关,如果需要比较精确的时间预估,建议在测试环境提前做测试。 可以新建一个测试实例,将备份数据导出到测试实例,执行 DDL 操作,判断执行时间,作为对线上执行的一个估计。但是请注意,该估计仍然可能不准确,因为线上实例的负载可能会比测试实例高。
-
负载 所有方式对大表做 DDL 都会增加负载,只是程度的不同,主要为 IO 的负载。如果是 IO 使用非常高的实例,建议在 IO 较小的时间段执行 DDL 操作。 如果 Online DDL 执行失败,则回滚有可能会是一项昂贵的操作;
-
额外空间占用 copy、inplace rebuild-table、gh-ost、pt-online-schema-change,都会将表完整复制一份出来再做 DDL 变更,因此会使用和原表空间一样大(甚至更大,如果是加列的操作的话)的额外空间,另外还会生成大量的临时日志。要特别注意剩余空间,确保空间充裕,不然可能导致 DDL 过程中磁盘写满。
-
临时日志文件大小(innodb_online_alter_log_max_size),当 DDL 执行过程当中允许并发执行 DML 操作时的日志大小需求。 临时排序文件大小(tmpdir),当 DDL 执行过程中表需要 rebuild 时临时排序文件是放在 tmpdir 指定的路径下的,需要保证该路径下的磁盘空间充足。 临时排序文件都足够容纳所有二级索引以及聚簇索引的主键列,最终合并到新表或索引后,临时排序文件会被删除。 在 MySQL5.7.11 及之后版本当中新增系统参数(innodb_tmpdir)专门用来指定 Online DDL 产生排序文件的路径。
临时中间表文件大小当有些 DDL 执行过程中表需要 rebuild 时会在当前表所在路径下产生临时中间表文件,临时中间表文件大小可能需要与原表大小一致,在 DDL 执行过程当中产生。
MDL 锁¶
所有方式做 DDL 均会产生 MDL(metadata lock)。除了 copy 模式会有持续性的锁(DDL 的整个过程期间无法向该表写入任何数据)之外,其他方式的 MDL 均为短暂的锁。
DDL 语法¶
ALTER TABLE tbl_name
[alter_specification [, alter_specification] ...]
[partition_options]
alter_specification:
table_options
| ADD [COLUMN] col_name column_definition
[FIRST | AFTER col_name]
| ADD [COLUMN] (col_name column_definition,...)
....
| ALGORITHM [=] {DEFAULT|INSTANT|INPLACE|COPY}
--备注:
-- 1.DEFAULT:MySQL自己选择锁定资源最少的方式
-- 2.INSTANT:只需要更新数据字典中的元数据, 很快完成
-- 3.INPLACE:此变更由InnoDB引擎独立完成, 不需要使用Redo log等, 可以节省开销
-- 4.COPY:此变更会重建聚簇索引, 执行DDL的时候会创建临时表
MySQL DDL 各种算法¶
copy 算法¶
这是早期较简单较低级的实现方法,MySQL
会在内部建立一个新的临时表,把源表的所有数据写入到临时表,在此期间无法对源表进行数据写入。
MySQL
在完成临时表的写入之后,用临时表替换掉源表。这个算法主要被早期(MySQL<=5.5)版本所使用。
MySQL 内部具体实现步骤如下:
- 按照表 A 的定义新建一个临时表 B
- 对表 A 加读锁,不允许执行 DML(期间表 A 只读)
- 在临时表 B 上执行 DDL 指定的操作(比如增加和删除列等)
- 逐行查出将表 A 中的数据查出来并插入到表 B
- 删除表 A
- 将表 B 重命名为 A
在 2-4 的过程中,如果表 A 数据量比较大,拷贝到表 B 的过程会消耗大量时间,并占用额外的存储空间。此外,由于 DDL 操作占用了表 A 的读锁,所以表 A 上的 DDL 和 DML 都将阻塞无法提供服务。
ALTER TABLE table1 ADD COLUMN column1 int ALGORITHM=COPY;
inplace 算法¶
当表发生 DDL 操作,可能会出现该表格数分钟甚至数小时不可访问,性能及响应异常,为了有效改善这个情况,MySQL 在 5.6.7 版本推出了Online DDL
新特性。
我们常说的 Online DDL
,其实是从 DML 操作的角度描述的,如果 DDL 操作不阻塞 DML,那么这个 DDL 就是 Online
的。
在Online DDL
中,也包含了copy
跟nplace
两种方式,对于不支持Online DDL
的DDL SQL
,则采用 COPY 方式;对于支持 Online DDL 的 DDL SQL,则采用 Inplace 方式,这里的 Inplace 又区分为 2 类:是否需要 rebuild 表格,判断标准为:是否修改行记录格式。
- 如果修改了行记录格式,则需要 rebuild 表格,比如修改列类型、增减列等;
- 如果没有修改行记录格式,仅修改表的元数据,则不需要 rebuild 表格,仅修改元数据 metadata,比如删除索引、设置默认值及重命名列名等。
它的过程和上面的 Copy 算法有些不同:
Online DDL
过程中,从表 A 提取 B+树,并存储到一个中间文件 tmp-file,而不是中间表 tmp-A- 步骤 1 执行过程中,对表 A 的写入,都会记录到 row log 中
- 步骤 1 执行完毕后,对 tmp-file 应用所有的 row log,得到一个与表 A 数据相同的数据文件
- 利用数据文件 tmp-file 替换表 A 的数据文件即可。
常用的 DDL 都默认使用这个算法。inplace 算法包含两类:inplace-no-rebuild
和 inplace-rebuild
,两者的主要差异在于是否需要重建源表。
存储引擎层"就地"重建表,虽然不阻塞 DML 操作,但对于大表变更可能导致长时间的主从不一致。对此类 DDL 语句,如果想使 DDL 过程更加可控,且对从库延迟比较敏感,建议使用第三方在线改表工具 ptosc 完成。