一、概述
在线DDL之快速增加列(秒级别的),并不会造成业务抖动。该功能自 MySQL 8.0.12 版本引入,是由腾讯游戏DBA团队贡献,此功能只适用于 InnoDB 表。实际上MySQL 5.7就已支持 Online DDL,虽说大部分 DDL 不影响对表DML操作,但是依然会消耗非常多的时间,且占用
额外的磁盘空间,并会造成主从延迟,或者影响表的查询速度。有了这个ALGORITHM=INSTANT 就可应对瞬息万变的需求了。
MySQL在大型表上的 DDL 会带来耗时较久、负载较高、额外空间占用、MDL、主从同步延时等情况。需要特别引起重视,而MySQL的DDL有很
多种方法。
MySQL本身自带三种方法,分别是:copy、inplace、instant。copy算法为最古老的算法,在MySQL5.5及以下为默认算法。
从MySQL5.6开始,引入了inplace算法并且默认使用。inplace算法还包含两种类型:rebuild-table 和 not-rebuild-table。
MySQL使用inplace算法时,会自动判断,能使用not-rebuild-table的情况下会尽量使用,不能的时候才会使用rebuild-table。
当DDL涉及到主键和全文索引相关的操作时,无法使用 not-rebuild-table,必须使用 rebuild-table。其他情况下都会使用 not-rebuild-table。
从MySQL8.0.12开始,引入了instant算法并且默认使用。目前instant算法只支持增加列等少量DDL类型的操作,其他类型仍然会默认使用inplace。
有一些第三方工具也可以实现DDL操作,最常见的是percona的pt-online-schema-change工具(简称为 pt-osc),和github的gh-ost工具,
均支持 MySQL 5.5 以上的版本。
》》各类工具的对比一般情况下的建议:如果使用的是 MySQL 5.5 或者 MySQL 5.6,推荐使用 gh-ost
如果使用的是 MySQL 5.7,索引等不涉及修改数据的操作,建议使用默认的 inplace 算法。如果涉及到修改数据(例如增加列),不关心主
从同步延时的情况下使用默认的 inplace 算法,关心主从同步延时的情况下使用 gh-ost
如果使用的是 MySQL 8.0,推荐使用 MySQL 默认的算法设置,在语句不支持 instant 算法并且在意主从同步延时的情况下使用gh-ost
二、MySQL DDL 的原理简析
1、copy 算法
较简单的实现方法,MySQL 会建立一个新的临时表,把源表的所有数据写入到临时表,在此期间无法对源表进行数据写入。MySQL在完成临时表的写入之后,用临时表替换掉源表。这个算法主要被早期(<=5.5)版本所使用。
2、inplace 算法
从 5.6 开始,常用的 DDL 都默认使用这个算法。inplace 算法包含两类:inplace-no-rebuild 和 inplace-rebuild,两者的主要差异在于是否需要重建源表。
inplace 算法的操作阶段主要分为三个:Prepare阶段: - 创建新的临时 frm 文件(与 InnoDB 无关)。 - 持有 EXCLUSIVE-MDL 锁,禁止读写。 - 根据 alter 类型,确定执行方式(copy,online-rebuild,online-not-rebuild)。 更新数据字典的内存对象。 - 分配 row_log 对象记录数据变更的增量(仅 rebuild 类型需要)。 - 生成新的临时ibd文件 new_table(仅rebuild类型需要)。
Execute 阶段:降级EXCLUSIVE-MDL锁,允许读写。扫描old_table聚集索引(主键)中的每一条记录 rec。遍历new_table的聚集索引和二级索引,逐一处理。根据 rec 构造对应的索引项。将构造索引项插入 sort_buffer 块排序。将 sort_buffer 块更新到 new_table 的索引上。记录 online-ddl 执行过程中产生的增量(仅 rebuild 类型需要)。重放 row_log 中的操作到 new_table 的索引上(not-rebuild 数据是在原表上更新)。重放 row_log 中的DML操作到 new_table 的数据行上。
Commit阶段:当前 Block 为 row_log 最后一个时,禁止读写,升级到 EXCLUSIVE-MDL 锁。重做 row_log 中最后一部分增量。更新 innodb 的数据字典表。提交事务(刷事务的 redo 日志)。修改统计信息。rename 临时 ibd 文件,frm文件。变更完成,释放 EXCLUSIVE-MDL 锁。
3、instant 算法
MySQL 8.0.12 才提出的新算法,目前只支持添加列等少量操作,利用 8.0 新的表结构设计,可以直接修改表的 metadata 数据,省掉了 rebuild 的过程,极大的缩短了 DDL 语句的执行时间。
4、pt-online-schema-change
借鉴了 copy 算法的思路,由外部工具来完成临时表的建立,数据同步,用临时表替换源表这三个步骤。其中数据同步是利用 MySQL 的触发器来实现的,会少量影响到线上业务的 QPS 及 SQL 响应时间。
三、mysql 8.0特性instant add column
1、instant add column原理
mysql数据库针对亿级别的大表加字段是痛苦的,需要对表进行重建,MySQL 5.7 支持 Online DDL,大部分 DDL 不影响对表的读取和写入,但是依然会消耗非常多的时间,且占用额外的磁盘空间,并会造成主从延迟。所以大表 DDL 仍是一件令 DBA 头痛的事。而mysql8.0使用instant ADD COLUMN特性,只需很短的时间,字段就加好了,享受MongoDB那样的非结构化存储的灵活方便,无形中减少了开发的工作量。
快速加列采用的是 instant 算法,使得添加列时不再需要 rebuild 整个表,只需要在表的 metadata 中记录新增列的基本信息即可。在 alter 语句后增加 ALGORITHM=INSTANT 即代表使用 instant 算法, 如果未明确指定,则支持 instant 算法的操作会默认使用。如果 ALGORITHM=INSTANT 指定但不支持,则操作立即失败并显示错误。
下表概述了对列操作的在线 DDL 支持。星号表示附加信息、异常或依赖项。
Operation
Instant
In Place
Rebuilds Table
Permits Concurrent DML
Only Modifies Metadata
Adding a column
Yes*
Yes
No*
Yes*
Yes
Dropping a column
Yes*
Yes
Yes
Yes
Yes
Renaming a column
Yes*
Yes
No
Yes*
Yes
Reordering columns
No
Yes
Yes
Yes
No
Setting a column default value
Yes
Yes
No
Yes
Yes
Changing the column data type
No
No
Yes
No
No
Extending VARCHAR column size
No
Yes
No
Yes
Yes
Dropping the column default value
Yes
Yes
No
Yes
Yes
Changing the auto-increment value
No
Yes
No
Yes
No*
Making a column NULL
No
Yes
Yes*
Yes
No
Making a column NOT NULL
No
Yes*
Yes*
Yes
No
Modifying the definition of an ENUM or SET column
Yes
Yes
No
Yes
Yes
三、语法和使用说明
1、添加列
ALTER TABLE tbl_name ADD COLUMN column_name column_definition, ALGORITHM=INSTANT;INSTANT是MySQL8.0.12和INPLACE之后的默认算法。
当 INSTANT算法用于添加列时,以下限制适用:
1、添加列不能与ALTER TABLE不支持的其他操作在同一语句中组合ALGORITHM=INSTANT。
2、在MySQL 8.0.29之前,一列只能作为表的最后一列添加。不支持将列添加到其他列中的任何其他位置。从MySQL 8.0.29开始,可以将即时
添加的列添加到表中的任何位置。
3、不能将列添加到使用ROW_FORMAT=COMPRESSED的表、具有FULLTEXT索引的表、驻留在数据字典表空间中的表或临时表。临时表仅支持
ALGORITHM=COPY.
4、在MySQL 8.0.29之前,添加列时不会评估行大小限制。但是,在插入和更新表中的行的DML操作期间会检查行大小限制。从8.0.29开始,
添加列时会检查行大小限制。如果超出限制,则会报告以下错误消息:
"ERROR 4092 (HY000): Column can't be added with ALGORITHM=INSTANT as after this max possible row size crosses max permissible row size.
Try ALGORITHM=INPLACE/COPY."
可以在同一 ALTER TABLE语句中添加多个列。例如:
ALTER TABLE t1 ADD COLUMN c2 INT, ADD COLUMN c3 INT, ALGORITHM=INSTANT;
ALTER TABLE ... ALGORITHM=INSTANT在每次添加一或多列、删除一或多列或在同一操作中添加和删除一或多列的操作之后,都会创建一个新
的行版本 。该INFORMATION_SCHEMA.INNODB_TABLES.TOTAL_ROW_VERSIONS列跟踪表的行版本数。每次立即添加或删除列时,该值都会增加
。初始值为 0。
mysql> SELECT NAME, TOTAL_ROW_VERSIONS FROM INFORMATION_SCHEMA.INNODB_TABLES
WHERE NAME LIKE 'test/t1';
当通过表重建ALTER TABLE 或OPTIMIZE TABLE 操作重建具有即时添加或删除列的表时,该TOTAL_ROW_VERSIONS值重置为 0。允许的最大
行版本数为 64,因为每个行版本都需要额外的空间用于表元数据。当达到行版本限制时,ADD COLUMN or DROP COLUMN
using ALGORITHM=INSTANT被拒绝,并显示一条错误消息,建议使用 COPY or INPLACE 算法重建表。
'ERROR 4080 (HY000): Maximum row versions reached for table test/t1. No more columns can be added or dropped instantly. Please use COPY/INPLACE.'
2、删除一列
ALTER TABLE tbl_name DROP COLUMN column_name, ALGORITHM=INSTANT;当 INSTANT算法用于删除列时,以下限制适用:
1、删除列不能与ALTER TABLE不支持的其他操作在同一语句中组合ALGORITHM=INSTANT。
2、不能从使用 ROW_FORMAT=COMPRESSED的表、具有 FULLTEXT索引的表、驻留在数据字典表空间中的表或临时表中删除列。临时表仅支持
ALGORITHM=COPY.
ALTER TABLE同一语句中可以删除多个列;例如:
ALTER TABLE t1 DROP COLUMN c4, DROP COLUMN c5, ALGORITHM=INSTANT;
每次使用 添加或删除列时ALGORITHM=INSTANT,都会创建一个新的行版本。该INFORMATION_SCHEMA.INNODB_TABLES.TOTAL_ROW_VERSIONS
列跟踪表的行版本数。每次立即添加或删除列时,该值都会增加。初始值为 0。
3、重命名列
ALTER TABLE tbl CHANGE old_col_name new_col_name data_type, ALGORITHM=INSTANT, LOCK=NONE;MySQL8.0.28中添加了ALGORITHM=INSTANT对重命名列的支持。早期的MySQL服务器版本仅支持ALGORITHM=INPLACE or ALGORITHM=COPY命名列。
要允许并发 DML,请保持相同的数据类型并仅更改列名。
当您保持相同的数据类型和[NOT] NULL属性时,只需更改列名,就可以始终在线执行操作。
仅允许重命名从另一个表引用的列ALGORITHM=INPLACE。如果您使用ALGORITHM=INSTANTor ALGORITHM=COPY或其他操作使用这些算法的条件
,则 ALTER TABLE语句将失败。
ALGORITHM=INSTANT支持重命名虚拟列;ALGORITHM=INPLACE不支持。
ALGORITHM=INSTANT和ALGORITHM=INPLACE不支持在同一语句中添加或删除虚拟列时重命名列。在这种情况下仅ALGORITHM=COPY支持。
4、更改列数据类型
ALTER TABLE tbl_name CHANGE c1 c1 BIGINT, ALGORITHM=COPY;仅支持更改列数据类型 ALGORITHM=COPY。
5、扩展VARCHAR列大小
ALTER TABLE tbl_name CHANGE COLUMN c1 c1 VARCHAR(255), ALGORITHM=INPLACE, LOCK=NONE;列所需的长度字节数VARCHAR必须保持不变。对于VARCHAR大小为0到255字节的列,需要一个长度字节来对值进行编码。对于VARCHAR大小为
256 字节或更大的列,需要两个长度字节。因此,INPLACE ALTER TABLE仅支持将VARCHAR列大小从0字节增加到255字节,或从256字节增
加到更大的大小。INPLACE ALTER TABLE不支持增加VARCHAR列从小于256字节到大小等于或大于256字节。在这种情况下,所需的长度字节
数从1变为2,仅表拷贝 ( ALGORITHM=COPY) 支持。例如,尝试VARCHAR使用就地将单字节字符集的列大小从VARCHAR(255)更改为
VARCHAR(256) 会ALTER TABLE返回此错误:
ALTER TABLE tbl_name ALGORITHM=INPLACE, CHANGE COLUMN c1 c1 VARCHAR(256);
ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
不支持VARCHAR使用就地减小大小。ALTER TABLE减小VARCHAR大小需要使用ALGORITHM=COPY
6、设置列默认值
ALTER TABLE tbl_name ALTER COLUMN col SET DEFAULT literal, ALGORITHM=INSTANT;仅修改表元数据。默认列值存储在数据字典中。
7、删除列默认值
ALTER TABLE tbl ALTER COLUMN col DROP DEFAULT, ALGORITHM=INSTANT;8、更改自动增量值
ALTER TABLE table AUTO_INCREMENT=next_value, ALGORITHM=INPLACE, LOCK=NONE;修改存储在内存中的值,而不是数据文件。
在使用复制或分片的分布式系统中,您有时会将表的自动增量计数器重置为特定值。插入到表中的下一行使用其自动增量列的指定值。您也可
以在数据仓库环境中使用此技术,在该环境中,您会定期清空所有表并重新加载它们,然后从 1 重新启动自动递增序列。
9、创建NULL列
ALTER TABLE tbl_name MODIFY COLUMN column_name data_type NULL, ALGORITHM=INPLACE, LOCK=NONE;就地重建表。数据被大量重组,使其成为一项昂贵的操作。
10、创建NOT NULL列
ALTER TABLE tbl_name MODIFY COLUMN column_name data_type NOT NULL, ALGORITHM=INPLACE, LOCK=NONE;就地重建表。 STRICT_ALL_TABLES或者 STRICT_TRANS_TABLES SQL_MODE是操作成功所必需的。如果列包含 NULL 值,则操作失败。服务
器禁止对可能导致失去参照完整性的外键列进行更改。
11、修改ENUM或 SET列 的定义
CREATE TABLE t1 (c1 ENUM('a', 'b', 'c'));ALTER TABLE t1 MODIFY COLUMN c1 ENUM('a', 'b', 'c', 'd'), ALGORITHM=INSTANT;
只要数据类型的存储大小不变,就可以立即或就地执行通过在有效成员值列表的末尾添加新枚举或集合成员来 修改ENUM或列的定义。
例如,将成员添加到具有8个成员的列会将每个值所需的存储空间从1个字节更改为2个字节;这需要一个表格副本。在列表中间添加成员会导致
对现有成员重新编号,这需要一个表拷贝。
12、外键操作
Operation
Instant
In Place
Rebuilds Table
Permits Concurrent DML
Only Modifies Metadata
Adding a foreign key constraint
No
Yes*
No
Yes
Yes
Dropping a foreign key constraint
No
Yes
No
Yes
Yes
四、补充
更多信息可以查看mysql官方文档
https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html