在 MySQL 5.1 (带InnoDB Plugin)和5.5中,有个新特性叫 Fast Index Creation(下称 FIC),就是在添加或者删除二级索引的时候,能够不用复制原表。对于以前的版本对于索引的添加删除这类DDL操做,MySQL数据库的操做过程为以下:html
为了保持数据的一致性,中间复制数据(Copy Table)全程锁表只读,若是有写请求进来将没法提供服务,链接数爆张。mysql
引入FIC以后,建立二级索引时会对原表加上一个S锁,建立过程不须要重建表(no-rebuild);删除InnoDB二级索引只须要更新内部视图,并标记这个索引的空间可用,去掉数据库元数据上该索引的定义便可。这个过程也只容许读操做,不能写入,但大大加快了修改索引的速度(不含主键索引,InnoDB IOT的特性决定了修改主键依然须要 Copy Table )。sql
FIC只对索引的建立删除有效,MySQL 5.6 Online DDL把这种特性扩展到了添加列、删除列、修改列类型、列重命名、设置默认值等等,实际效果要看所使用的选项和操做类别来定。数据库
MySQL 在线DDL分为 INPLACE
和 COPY
两种方式,经过在ALTER语句的ALGORITHM参数指定。缓存
ALGORITHM=INPLACE
,能够避免重建表带来的IO和CPU消耗,保证ddl期间依然有良好的性能和并发。ALGORITHM=COPY
,须要拷贝原始表,因此不容许并发DML写操做,可读。这种copy方式的效率仍是不如 inplace ,由于前者须要记录undo和redo log,并且由于临时占用buffer pool引发短期内性能受影响。上面只是 Online DDL 内部的实现方式,此外还有 LOCK 选项控制是否锁表,根据不一样的DDL操做类型有不一样的表现:默认mysql尽量不去锁表,可是像修改主键这样的昂贵操做不得不选择锁表。并发
LOCK=NONE
,即DDL期间容许并发读写涉及的表,好比为了保证 ALTER TABLE 时不影响用户注册或支付,能够明确指定,好处是若是不幸该 alter语句不支持对该表的继续写入,则会提示失败,而不会直接发到库上执行。ALGORITHM=COPY
默认LOCK级别LOCK=SHARED
,即DDL期间表上的写操做会被阻塞,但不影响读取。LOCK=DEFAULT
,让mysql本身去判断lock的模式,原则是mysql尽量不去锁表LOCK=EXCLUSIVE
,即DDL期间该表不可用,堵塞任何读写请求。若是你想alter操做在最短的时间内完成,或者表短期内不可用能接受,能够手动指定。可是有一点须要说明,不管任何模式下,online ddl开始以前都须要一个短期排它锁(exclusive)来准备环境,因此alter命令发出后,会首先等待该表上的其它操做完成,在alter命令以后的请求会出现等待waiting meta data lock
。一样在ddl结束以前,也要等待alter期间全部的事务完成,也会堵塞一小段时间。因此尽可能在ALTER TABLE以前确保没有大事务在执行,不然同样出现连环锁表。app
从上面的介绍能够看出,不是5.6支持在线ddl就能够为所欲为的alter table,锁不锁表要看状况:less
提示:下表根据官方 Summary of Online Status for DDL Operations 整理挑选的经常使用操做。性能
Operation | In-Place? | Copies Table? | Allows Concurrent DML? | Allows Concurrent Query? | Notes |
---|---|---|---|---|---|
添加索引 | Yes* | No* | Yes | Yes | 对全文索引的一些限制 |
删除索引 | Yes | No | Yes | Yes | 仅修改表的元数据 |
OPTIMIZE TABLE | Yes | Yes | Yes | Yes | 从 5.6.17开始使用ALGORITHM=INPLACE,固然若是指定了old_alter_table=1 或mysqld启动带--skip-new 则将仍是COPY模式。若是表上有全文索引只支持COPY |
对一列设置默认值 | Yes | No | Yes | Yes | 仅修改表的元数据 |
对一列修改auto-increment 的值 | Yes | No | Yes | Yes | 仅修改表的元数据 |
添加 foreign key constraint | Yes* | No* | Yes | Yes | 为了不拷贝表,在约束建立时会禁用foreign_key_checks |
删除 foreign key constraint | Yes | No | Yes | Yes | foreign_key_checks 不影响 |
改变列名 | Yes* | No* | Yes* | Yes | 为了容许DML并发, 若是保持相同数据类型,仅改变列名 |
添加列 | Yes* | Yes* | Yes* | Yes | 尽管容许 ALGORITHM=INPLACE ,但数据大幅重组,因此它仍然是一项昂贵的操做。当添加列是auto-increment,不容许DML并发 |
删除列 | Yes | Yes* | Yes | Yes | 尽管容许 ALGORITHM=INPLACE ,但数据大幅重组,因此它仍然是一项昂贵的操做 |
修改列数据类型 | No | Yes* | No | Yes | 修改类型或添加长度,都会拷贝表,并且不容许更新操做 |
更改列顺序 | Yes | Yes | Yes | Yes | 尽管容许 ALGORITHM=INPLACE ,但数据大幅重组,因此它仍然是一项昂贵的操做 |
修改ROW_FORMAT 和KEY_BLOCK_SIZE |
Yes | Yes | Yes | Yes | 尽管容许 ALGORITHM=INPLACE ,但数据大幅重组,因此它仍然是一项昂贵的操做 |
设置列属性NULL 或NOT NULL |
Yes | Yes | Yes | Yes | 尽管容许 ALGORITHM=INPLACE ,但数据大幅重组,因此它仍然是一项昂贵的操做 |
添加主键 | Yes* | Yes | Yes | Yes | 尽管容许 ALGORITHM=INPLACE ,但数据大幅重组,因此它仍然是一项昂贵的操做。 若是列定义必须转化NOT NULL,则不容许INPLACE |
删除并添加主键 | Yes | Yes | Yes | Yes | 在同一个 ALTER TABLE 语句删除就主键、添加新主键时,才容许inplace;数据大幅重组,因此它仍然是一项昂贵的操做。 |
删除主键 | No | Yes | No | Yes | 不容许并发DML,要拷贝表,并且若是没有在同一 ATLER TABLE 语句里同时添加主键则会收到限制 |
变动表字符集 | No | Yes | No | Yes | 若是新的字符集编码不一样,重建表 |
从表看出,In-Place为No,DML必定是No,说明 ALGORITHM=COPY
必定会发生拷贝表,只读。但 ALGORITHM=INPLACEE
也要可能发生拷贝表,但能够并发DML:优化
不容许并发DML的状况有:修改列数据类型、删除主键、变动表字符集,即这些类型操做ddl是不能online的。
另外,更改主键索引与普通索引处理方式是不同的,主键即汇集索引,体现了表数据在物理磁盘上的排列,包含了数据行自己,须要拷贝表;而普通索引经过包含主键列来定位数据,因此普通索引的建立只须要一次扫描主键便可,并且是在已有数据的表上创建二级索引,更紧凑,未来查询效率更高。
修改主键也就意味着要重建全部的普通索引。删除二级索引更简单,修改InnoDB系统表信息和数据字典,标记该因此不存在,标记所占用的表空间能够被新索引或数据行从新利用。
datadir
目录有足够的磁盘空间,可以放的下整张表,由于拷贝表的的操做是直接在数据目录下进行的。tmpdir
目录足够存下索引一列的数据(若是是组合索引,当前临时排序文件一合并到原表上就会删除)innodb_online_alter_log_max_size
变量所指定的大小,会引发 DB_ONLINE_LOG_TOO_BIG 错误。默认为 128M,特别对于须要拷贝大表的alter操做,考虑临时加大该值,以此得到更大的日志缓存空间ALTER TABLE
以后,最好 ANALYZE TABLE tb1
去更新索引统计信息online ddl主要包括3个阶段,prepare阶段,ddl执行阶段,commit阶段,rebuild方式比no-rebuild方式实质多了一个ddl执行阶段,prepare阶段和commit阶段相似。下面将主要介绍ddl执行过程当中三个阶段的流程。
Prepare阶段 :
ddl执行阶段 :
commit阶段 :
1、打开功能
mysql> UPDATE setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'stage/innodb/alter%'; Query OK, 7 rows affected (0.00 sec) Rows matched: 7 Changed: 7 Warnings: 0 mysql> UPDATE setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%stages%'; Query OK, 3 rows affected (0.00 sec) Rows matched: 3 Changed: 3 Warnings: 0
2、执行改表操做
mysql> ALTER TABLE employees.employees ADD COLUMN middle_name varchar(14) AFTER first_name; Query OK, 0 rows affected (9.27 sec) Records: 0 Duplicates: 0 Warnings: 0
3、查看改表进度
mysql> SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM events_stages_current; +------------------------------------------------------+----------------+----------------+ | EVENT_NAME | WORK_COMPLETED | WORK_ESTIMATED | +------------------------------------------------------+----------------+----------------+ | stage/innodb/alter table (read PK and internal sort) | 280 | 1245 | +------------------------------------------------------+----------------+----------------+ 1 row in set (0.01 sec)