正文html
MySQL Online DDL这个新特性是在MySQL5.6.7开始支持的,更早期版本的MySQL进行DDL对于DBA来讲是很是痛苦的。如今主流版本都集中在5.6与5.7,为了更好的理解Online DDL的工做原理与机制,本文就对Online DDL的实现方式进行总结。mysql
本文使用的MySQL版本为官方社区版 5.7.24
。sql
(root@localhost) [test] > select version(); +------------+ | version() | +------------+ | 5.7.24-log | +------------+ 1 row in set (0.00 sec)
主要说明
Online DDL这个新特性解决了早期版本MySQL进行DDL操做同时带来锁表的问题,在DDL执行的过程中依然能够保证读写状态,不影响数据库对外提供服务,大大提升了数据库和表维护的效率。数据库
- 早期实现方式(MySQL5.6.7以前版本)
早期版本MySQL执行DDL语句时主要经过如下方式进行:服务器
COPY方式:
这是InnoDB最先期支持的方式,主要实现步骤:并发
- 建立与原表结构定义一致的临时表;
- 对原表加锁,不容许执行DML,但容许查询;
- 在临时表上执行DDL语句;
- 逐行拷贝原表数据到临时表;
- 原表与临时表进行RENAME操做,此时会升级原表上的锁,不容许读写,直至完成DDL操做;
INPLACE方式:
INPLACE方式也称为InnoDB fast index creation,是MySQL5.5及以后版本为了提升建立二级索引效率的方式,因此INPLACE方式仅限于二级索引的建立跟删除,关于fast index creation能够参考官方文档:InnoDB fast index creation,主要实现步骤:性能
- 建立临时的frm文件;
- 对原表加锁,不容许执行DML,但容许查询;
- 根据汇集索引的顺序,构造新的索引项,按照顺序插入新索引页;
- 升级原表上的锁,不容许读写操做;
- 进行RENAME操做,替换原表的frm文件,完成DDL操做。
相对于COPY方式,INPLACE方式在原表上进行,不会生成临时表,也不会拷贝原表数据,减小了不少系统I/O资源占用,但仍是没法进行DML操做,也只适用于索引的建立与删除,并不适用于其余类型的DDL语句。学习
- 当前实现方式(MySQL5.6.7及以后版本)
在MySQL5.6.7及以后版本中推出了新的特性:优化
Online DDL方式:
Online DDL特性是基于MySQL5.5的InnoDB fast index creation上改进加强的。Online DDL一样包含两种方式:ui
- COPY方式;
- INPLACE方式。
其中,某些DDL语句不支持Online DDL的就采用COPY方式,支持Online DDL的则采用INPLACE方式,由于Online DDL是对早期INPLACE方式的增长,因此INPLACE方式根据是否涉及到记录格式的修改又分为以下两种情形:
- Rebuilds Table;
- No-Rebuilds Table。
Rebuilds Table操做是由于DDL有涉及到行记录格格式的修改,如字段的增、删、类型修改等;
No-Rebuilds Table则不涉及行记录格式的修改,如索引删除、字段名修改等。
Online DDL选项
ALGORITHM={COPY|INPLACE}
指定DDL执行时对表的操做方式。首选是INPLACE,但并不是全部的语句都支持INPLACE,须要根据DDL语句类型决定。- LOCK={NONE|SHARED|DEFAULT|EXCLUSIVE}
指定DDL执行时对表锁定方式。默认状况下MySQL在表执行DDL时会尽可能使用最少的锁定,LOCK选项能够为DDL语句指定执行更为严格的锁定方式,一旦指定的锁级别低于DDL语句执行所需的锁级别,则DDL语句会执行失败。- NONE:容许并发查询和DML操做;
- SHARED:容许并发查询,但不容许DML操做;
- DEFAULT:容许尽量多的并发查询或DML操做(或二者都容许),没指定LOCK选项默认就为DEFAULT;
- EXCLUSIVE:不容许并发查询和DML操做。
Online DDL类型
根据官方文档Online DDL Operations的描述,结合常见的表DDL执行语句,MySQL5.7支持的Online DDL操做类型主要有如下种类:
- 索引操做
- 字段操做
- 组合字段操做
- 主键操做
- 外键操做
- 表操做
- 表分区操做
- 表空间操做
索引操做
索引操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
建立添加二级索引 | YES | NO | YES | NO |
重命名索引 | YES | NO | YES | YES |
删除索引 | YES | NO | YES | YES |
建立全文索引 | YES | NO | NO | NO |
建立空间索引 | YES | NO | NO | NO |
修改索引类别 | YES | NO | YES | YES |
由以上表格能够看出涉及索引的DDL操做均可以使用INPLACE方式来完成,除了建立全文索引与空间索引以外都容许DML操做,并不会锁表。
-- 建立添加二级索引 create index index_name on table_name (column[,column]..); 或 alter table table_name add index index_name (column[,column]..); -- 删除索引 drop index index_name on table_name; 或 alter table table_name drop index index_name; -- 重命名索引 alter table table_name rename index old_index_name to new_index_name, algorithm=inplace, lock=none; -- 建立全文索引 create fulltext index index_name on table_name(column[,column]..); -- 建立空间索引 create table geom (g geometry not null); alter table geom add spatial index(g), algorithm=inplace, lock=none; -- 修改索引类型 alter table table_name drop index index_name, add index index_name(column[,column]..) using btree, algorithm=inplace;
字段操做
字段操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
添加字段 | YES | YES | YES | NO |
删除字段 | YES | YES | YES | NO |
重命名字段 | YES | NO | YES | YES |
重排序字段 | YES | YES | YES | NO |
字段指定默认值 | YES | NO | YES | YES |
字段修改类型 | NO | YES | NO | NO |
扩展VARCHAR字段大小 | YES | NO | YES | YES |
删除字段默认值 | YES | NO | YES | YES |
修改自增值 | YES | NO | YES | NO |
字段指定NULL | YES | YES | YES | NO |
字段指定NOT NULL | YES | YES | YES | NO |
修改枚举(ENUM OR SET)定义值 | YES | NO | YES | YES |
从以上表格能够看出,除了修改字段类型的DDL语句没法使用INPLACE方式外,其余均可以使用到INPLACE方式执行DDL语句。
-- 添加字段,若是添加的是自增列,仍是不容许DML操做 alter table table_name add column column_name(column_definition), algorithm=inplace, lock=none; -- 删除字段 alter table table_name drop column column_name, algorithm=inplace, lock=none; -- 重命名字段 /* 为了容许并发DML操做,需保持重命名后字段类型一致,只修改字段名; 若是重命名的字段在外键定义中,外键定义也会自动更新为新字段名。 */ alter table table_name change old_column_name new_column_name data_type, algorithm=inplace, lock=none; -- 重排序字段,使用first或after after table table_name modify column column_name column_definition first, algorithm=inplace, lock=none; -- 字段修改类型,只支持COPY方式 alter table table_name change column column column_definition, algorithm=copy; -- 扩展VARCHAR字段大小 /* 当varchar字节长度为0~255时,须要额外一个字节进行编码; 当varchar字节长度大于255时,则须要额外两个字节进行编码; 当varchar字节长度在0~255之间时,而且需从小变大的状况,支持INPLACE方式; 当varchar字节长度字节编码数从1变为2或者从2变为1时,则须要用COPY方式。 */ alter table table_name change column column column_definition, algorithm=inplace, lock=none; -- 例表: t(c1 varchar(20)) alter table t change c1 c1 varchar(85), algorithm=inplace, lock=none; alter table t change c1 c1 varchar(10), algorithm=copy; alter table t change c1 c1 varchar(100), algorithm=copy; -- 字段指定默认值 alter table table_name alter column column_name set default literal, algorithm=inplace, lock=none; -- 删除字段默认值 alter table table_name alter column column_name drop default, algorithm=inplace, lock=none; -- 修改自增列值 alter table table_name auto_increment=next_value, algorithm=inplace, lock=none; -- 字段指定NULL alter table table_name modify column column_name data_type NULL, algorithm=inplace, lock=none; -- 字段指定NOT NULL -- sql_mode中需包含选项STRICT_TRANS_TABLES和STRICT_ALL_TABLES才能使用INPLACE,不然需使用COPY alter table table_name modify column column_name data_type NOT NULL, algorithm=inplace, lock=none; -- 修改枚举(ENUM OR SET)定义值 -- 例表: t (c2 enum('a','b','c')) alter table t modify column c2 enum('a','b','c','d'), algorithm=inplace, lock=none;
组合字段操做
组合字段操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
添加存储(STORED)组合字段 | NO | YES | NO | NO |
删除存储(STORED)组合字段 | YES | YES | YES | NO |
修改存储(STORED)组合字段顺序 | NO | YES | NO | NO |
添加虚拟(VIRTUAL)组合字段 | YES | NO | YES | YES |
删除虚拟(VIRTUAL)组合字段 | YES | NO | YES | YES |
修改虚拟(VIRTUAL)组合字段顺序 | NO | YES | NO | NO |
-- 例表: t (c1 int,c2 varchar(20)) -- 添加存储组合字段 alter table t add column (c3 int generated always as (c1 + 2) stored), algorithm=copy; -- 删除存储组合字段 alter table t drop column c3, algorithm=inplace, lock=none; -- 修改存储组合字段顺序 alter table t modify column c3 int generated always as (c1 + 1) stored first, algorithm=copy; -- 添加虚拟组合字段 -- 对于非分区表才可使用INPLACE方式,不能与其余的alter table语句合并使用 alter table t add column (c3 int generated always as (c1 + 1) virtual), algorithm=inplace, lock=none; -- 删除虚拟组合字段 -- 对于非分区表才可使用INPLACE方式,不能与其余的alter table语句合并使用 alter table t drop column c3, algorithm=inplace, lock=none; -- 修改虚拟组合字段顺序 alter table t modify column c3 int generated always as (c1 + 1) virtual first, algorithm=copy;
主键操做
主键操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
添加主键 | YES | YES | YES | NO |
删除主键 | NO | YES | NO | NO |
删除主键并添加新主键 | YES | YES | YES | NO |
-- 例表: t (c1 int,c2 varchar(20)) -- 添加主键 alter table t add primray key (c1),algorithm=inplace, lock=none; 采用INPLACE方式进行表数据重构,若是添加主键涉及字段没有NOT NULL属性时,则没法使用INPLACE方式,只能使用COPY方式。InnoDB表为索引组织表,当涉及到汇集索引的从新构建时须要对表中数据进行拷贝,为了减小性能开销,最好在建表时就指定主键。 由于InnoDB表的特殊性,DDL操做主键必定会涉及到表行数据的拷贝操做,但经过INPLACE方式添加比COPY方式添加主要有以下优点: 1.不须要记录undo和redo日志,记录日志会提高性能开销; 2.二级索引数据行是预先排序的,能够按顺序加载; 3.无需使用到change buffer,由于没有随机数据插入二级索引当中。 -- 删除主键 alter table t drop primary key,algorithm=copy; -- 删除主键并添加新主键 alter table t drop primary key,add primary key(c1,c2), algorithm=inplace, lock=none;
外键操做
外键操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
添加外键 | YES | NO | YES | YES |
删除外键 | NO | NO | YES | YES |
-- 添加外键 当系统参数foreign_key_checks = 0时,可使用INPLACE方式,不然,只能使用COPY方式。 alter table t1 add constraint fk_name foreign key index(col1) references t2(col2) referential_actions; -- 删除外键 alter table t drop foreign key fk_name;
表操做
表操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
修改行格式 | YES | YES | YES | NO |
修改索引键块大小 | YES | YES | YES | NO |
设置永久表统计信息 | YES | NO | YES | YES |
指定字符集 | YES | YES | NO | NO |
转换字符集 | NO | YES | NO | NO |
优化表格 | YES | YES | YES | NO |
使用FORCE选项重建表 | YES | YES | YES | NO |
使用NULL重建表 | YES | YES | YES | NO |
重命名表 | YES | NO | YES | YES |
-- 修改行格式 alter table table_name row_format = format, algorithm=inplace, lock=none; -- 修改索引键块大小 alter table table_name key_block_size = value, algorithm=inplace, lock=none; -- 设置永久表统计信息选项 alter table table_name stats_persistent = 0, stats_sample_pages = 20, stats_auto_recalc = 1, algorithm=inplace, lock=none; -- 优化表 -- 若是表中有全文索引,则不能使用INPLACE方式,不能使用algorithm和lock子句。 optimize table table_name; -- 使用FORCE选项重建表 -- 若是表中有全文索引,则不能使用INPLACE方式。 alter table table_name force, algorithm=inplace, lock=none; -- 使用NULL重建表 -- 若是表中有全文索引,则不能使用INPLACE方式。 alter table table_name engine = InnoDB, algorithm=inplace, lock=none; -- 重命名表 alter table table_name rename to new_table_name, algorithm=inplace, lock=none;
表分区操做
表分区操做类型以下表所示:
分区子句(Partitioning Clause) | 原表操做(In Place) | 容许DML操做(Permits DML) | 说明(Notes) |
---|---|---|---|
PARTITION BY | NO | NO | 只容许algorithm=copy,lock={default |
ADD PARTITION | NO | NO | 只容许algorithm=default,lock=default 执行期间对于已采用RANGE或LIST分区的数据不进行拷贝,对于已采用HASH或LIST分区的数据容许并发查询。在须要拷贝数据时持有共享锁。 |
DROP PARTITION | NO | NO | 只容许algorithm=default,lock=default 只容许algorithm=default,lock=default 只容许algorithm=default,lock=default 执行期间对于已采用RANGE或LIST分区的数据不进行拷贝。 |
DISCARD PARTITION | NO | NO | 只容许algorithm=default,lock=default |
IMPORT PARTITION | NO | NO | 只容许algorithm=default,lock=default |
TRUNCATE PARTITION | YES | YES | 不会对表中现有数据进行拷贝,仅仅删除分区数据行,不会改会表和表分区的定义。 |
COALESCE PARTITION | NO | NO | 只容许algorithm=default,lock=default 对于已采用HASH或LIST分区的数据容许并发查询。在须要拷贝数据时持有共享锁。 |
REORGANIZE PARTITION | NO | NO | 只容许algorithm=default,lock=default 对于已采用LINEAR HASH或LIST分区的数据容许并发查询。在从受影响分区拷贝数据时持有MDL锁。 |
EXCHANGE PARTITION | YES | YES | |
ANALYZE PARTITION | YES | YES | |
CHECK PARTITION | YES | YES | |
OPTIMIZE PARTITION | NO | NO | algorithm和lock子句被忽略。 |
REBUILD PARTITION | NO | NO | 只容许algorithm=default,lock=default 对于已采用LINEAR HASH或LIST分区的数据容许并发查询。在从受影响分区拷贝数据时持有MDL锁。 |
REPAIR PARTITION | YES | YES | |
REMOVE PARTITIONING | NO | NO | 只容许algorithm=copy,lock={default |
表空间操做
表空间操做类型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容许并发DML操做(Permits Concurrent DML) | 仅修改元数据(Only Modifies Metadata) |
---|---|---|---|---|
开启或禁止独立表空间加密 | NO | YES | NO | NO |
主要涉及独立表空间加密的Online DDL操做:
alter table table_name encryption='Y', algorithm=copy;
Online DDL过程
Online DDL主要有PREPARE(准备)、EXECUTE(执行)和COMMIT(提交)三个阶段,以下:
- PREPARE:
- 建立新的临时frm文件;
- 持有EXCLUSIVE-MDL锁,禁止读写操做;
- 根据ALTER类型,肯定执行方式(copy,Online-Rebuilds,Online-No-Rebuilds);
- 更新数据字典的内存对象;
- 分配row_log对象记录增量(Rebuilds须要);
- 生成新的临时ibd文件(Rebuilds须要)。
- EXECUTE:
- 降级EXCLUSIVE-MDL锁,容许读写;
- 记录执行期间产生的DML增量到row_log中(Rebuilds须要);
- 扫描old_table的汇集索引中每一条记录record;
- 遍历新表的汇集索引和二级索引,逐一处理;
- 根据record构造对应的索引项;
- 将构造的索引项插入sort_buffer块中;
- 将sort_buffer块插入到新的索引中;
- 将row_log中的记录应用到新临时表中,应用到最后一个block;
- COMMIT:
- 升级到EXECLUSIVE-MDL锁,禁止读写;
- 重作row_log中最后一部分的增量;
- 更新InnoDB的数据字典表;
- 提交事务,写InnoDB redo日志;
- 修改统计信息;
- RENAME临时的ibd和frm文件;
- 执行变动完成。
row_log记录了DDL执行期间产生的DML操做,这保证了变动期间表的并发性,经过以上过程能够看出在EXECUTE(执行)阶段表容许读写操做,操做记录在row_log中,在最后阶段应用到新表当中,保证了数据的完整性。
Online DDL涉及参数
- old_alter_table
属性(Property) | 值(Value) |
---|---|
命令行格式(Command-Line Format) | --old-alter-table |
系统变量格式(System Variable) | old_alter_table |
做用范围(Scope) | 全局、会话 |
动态参数(Dynamic) | 是 |
类型(Type) | 布尔型 |
默认值(Default Value) | OFF |
指定是否使用早期版本的DDL方式,默认为OFF,为动态参数,能够全局和会话级别修改。指定表DDL的执行过程中采用COPY方式生成临时表复制数据。
- innodb_online_alter_log_max_size
属性(Property) | 值(Value) |
---|---|
命令行格式(Command-Line Format) | --innodb-online-alter-log-max-size=# |
系统变量格式(System Variable) | innodb_online_alter_log_max_size |
做用范围(Scope) | 全局 |
动态参数(Dynamic) | 是 |
类型(Type) | 数值型 |
默认值(Default Value) | 134217728 |
最小值(Minimum Value) | 65536 |
最大值(Maximum Value) | 2**64-1 |
指定Online DDL执行期间产生临时日志文件的最大大小,单位字节,默认大小为128MB。日志文件记录的是表在DDL期间的数据插入、更新和删除信息(DML操做),一旦日志文件超过该参数指定值时,DDL执行就会失败并回滚全部未提交的当前DML操做,因此,当执行DDL期间有大量DML操做时能够提升该参数值,但同时也会增长DDL执行完成时应用日志时锁定表的时间。
Online DDL注意事项
对于线上环境的MySQL来讲,任何类型的DDL都要十分谨慎,最好在语句执行以前能够分析下语句所使用的方式以及预估判断下影响的时长,尽可能选择在业务访问的低峰期进行操做,主要有如下几点须要注意:
- 空间需求
- 临时日志文件大小(innodb_online_alter_log_max_size)
当DDL执行过程中容许并发执行DML操做时的日志大小需求。 - 临时排序文件大小(tmpdir)
当DDL执行过程当中表须要rebuild时临时排序文件是放在tmpdir指定的路径下的,须要保证该路径下的磁盘空间充足。临时排序文件都足够容纳全部二级索引以及聚簇索引的主键列,最终合并到新表或索引后,临时排序文件会被删除。在MySQL5.7.11及以后版本当中新增系统参数innodb_tmpdir专门用来指定Online DDL产生排序文件的路径。 - 临时中间表文件大小
当有些DDL执行过程当中表须要rebuild时会在当前表所在路径下产生临时中间表文件,临时中间表文件大小可能须要与原表大小一致,在DDL执行过程中产生。
- 临时日志文件大小(innodb_online_alter_log_max_size)
合并拆分同表的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语句操做。
总结
总之,对于线上环境的DDL语句执行一样也须要保持敬畏之心,不管执行的DDL语句是复杂仍是简单,最好能够评估下执行成本,还有须要选择在业务低峰期进行操做。
参考
https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl.html
https://dev.mysql.com/doc/refman/5.5/en/innodb-create-index.html
http://www.cnblogs.com/cchust/p/4639397.html
http://www.cnblogs.com/xinysu/p/6732646.html
http://hedengcheng.com/?p=421
☆〖本人水平有限,文中若有错误还请留言批评指正!〗☆