onlineDDL语法:
alter table
ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}
ADD [COLUMN] col_name column_definition [FIRST|AFTER col_name]
CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name]
MODIFY [COLUMN] col_name column_definition
[FIRST | AFTER col_name],
ALGORITHM [=] {DEFAULT|INPLACE|COPY} LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}; 简单的说就是原来的语句上,加个ALGORITHM=xxx LOCK=XXXX
ALGORITHM指定了onlineDDL时候是使用COPY,还是INPLACE,
()COPY表示执行DDL的时候会创建临时表。
()INPLACE表示不需要创建临时表。(inplace英文单词是原地的意思)
()DEFAULT表示根据参数old_alter_table来判断是通过INPLACE还是COPY的算法,old_alter_table参数默认为OFF,表示采用INPLACE的方式 LOCK部分为索引创建或删除时对表添加锁的情况,默认是default,可选择的如下:
()NONE,目标表不添加任何锁,可以进行读写操作,不阻塞任何操作。如果手工指定NONE,但是onlineDDL不支持NONE模式,返回一个错误信息,告诉你用SHARE模式。 ()SHARE,对操作表加一个S锁。不阻塞读操作。写操作会阻塞,将会发生等待MDL锁,如果手工指定SHARE,但是onlineDDL不支持SHARE模式,将返回一个错误信息。 ()EXCLUSIVE,执行索引创建或删除时,对目标表加上一个X锁。读写事务均不能进行。会阻塞所有的线程。这和COPY方式类似,但是不需要像COPY方式那样创建一张临时表。 ()DEFAULT,该模式首先会判断当前操作是否可以使用NONE模式,若不能,则判断是否可以使用SHARE模式,最后判断是否可以使用EXCLUSIVE模式。也就是说DEFAULT会通过判断事务的最大并发性来判断执行DDL的模式。 .创建一个测试表 (mysql5.-)root@localhost [test]> create table ddl_test (id int() not null,name varchar() not null default '',age int() null default ,primary key(id));
Query OK, rows affected (0.02 sec) (mysql5.-)root@localhost [test]> ###这边先看看onlineDDL语法,就是DDL语句后面价格ALGORITHM=INPLACE, lock=none,是否拷贝表,锁模式,什么都没指定默认为defalut
(mysql5.-)root@localhost [test]> ALTER TABLE `ddl_test` MODIFY COLUMN `id` int() NOT NULL AUTO_INCREMENT FIRST ,ALGORITHM=INPLACE, lock=none;
ERROR (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
(mysql5.-)root@localhost [test]> ALTER TABLE `ddl_test` MODIFY COLUMN `id` int() NOT NULL AUTO_INCREMENT FIRST ,ALGORITHM=copy, lock=none;
ERROR (0A000): LOCK=NONE is not supported. Reason: COPY algorithm requires a lock. Try LOCK=SHARED.
(mysql5.-)root@localhost [test]> ALTER TABLE `ddl_test` MODIFY COLUMN `id` int() NOT NULL AUTO_INCREMENT FIRST ,ALGORITHM=copy, lock=shared;
Query OK, rows affected (0.00 sec)
Records: Duplicates: Warnings: 创建一个大表 (mysql5.-)root@localhost [test]> insert into ddl_test (name,age) select name,age from ddl_test;
Query OK, rows affected ( min 20.73 sec)
Records: Duplicates: Warnings: ###onlineDDL,前不能针对该表的,有长事务,空闲未提交事务,不然会等待MDL锁 (简单的说,DML 要获取表MDL共享锁,DDL要获取MDL排他锁)
session (mysql5.-)root@localhost [test]> select count(name) from ddl_test; session
(mysql5.-)root@localhost [test]> alter table ddl_test add column address varchar() not null default '',ALGORITHM=INPLACE, lock=none; session
(mysql5.-)root@localhost [crm]> show processlist;
+----+---------+---------------------+------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+---------+---------------------+------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
| | root | localhost | test | Query | | optimizing | select count(name) from ddl_test |
| | root | localhost | test | Query | | Waiting for table metadata lock | alter table ddl_test add column address varchar() not null default '',ALGORITHM=INPLACE, lock=non |
| | root | localhost | crm | Query | | starting | show processlist |
| | odstest | 172.16.123.63: | NULL | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
+----+---------+---------------------+------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
rows in set (0.00 sec)
在等待MDL锁 .测试onlineDDL #增加列 session (mysql5.-)root@localhost [test]> alter table ddl_test add column address varchar() not null default '',ALGORITHM=INPLACE, lock=none; session (mysql5.-)root@localhost [test]> update ddl_test set name='adfadf' where id> and id <; session
看到木有,并不阻塞update (mysql5.-)root@localhost [crm]> show processlist;
+----+---------+---------------------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+---------+---------------------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| | root | localhost | test | Query | | updating | update ddl_test set name='adfadf' where id> and id < |
| | root | localhost | test | Query | | altering table | alter table ddl_test add column address varchar() not null default '',ALGORITHM=INPLACE, lock=non |
| | root | localhost | crm | Query | | starting | show processlist |
| | odstest | 172.16.123.63: | NULL | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
+----+---------+---------------------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
rows in set (0.00 sec)
删除一行数据试试,也不阻塞
(mysql5.-)root@localhost [test]> delete from ddl_test where id=;
Query OK, row affected (0.02 sec) (mysql5.-)root@localhost [test]> ####删除列 session 1
(mysql5.-)root@localhost [test]> alter table ddl_test drop column address,algorithm=inplace,lock=none;
session 2 #### session1--DDL ,执行之后执行。
(mysql5.7-101)root@localhost [test]> select count(name) from ddl_test;
+-------------+
| count(name) |
+-------------+
| 41943040 |
+-------------+
1 row in set (6.99 sec) session (mysql5.-)root@localhost [crm]> show processlist;
+----+---------+---------------------+------+---------+------+----------------+----------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+---------+---------------------+------+---------+------+----------------+----------------------------------------------------------------------+
| | root | localhost | test | Query | | optimizing | select count(name) from ddl_test |
| | root | localhost | test | Query | | altering table | alter table ddl_test drop column address,algorithm=inplace,lock=none |
| | root | localhost | crm | Query | | starting | show processlist |
| | odstest | 172.16.123.63: | NULL | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
| | odstest | 172.16.123.63: | test | Sleep | | | NULL |
+----+---------+---------------------+------+---------+------+----------------+----------------------------------------------------------------------+
rows in set (0.00 sec) 忘记了是DML,删除一行试一下,看会阻塞么,哈哈,并不阻塞(因为表数据很多,执行这个语句,会话二还在执行)
(mysql5.-)root@localhost [test]> delete from ddl_test where id=;
Query OK, rows affected (0.02 sec) (mysql5.-)root@localhost [test]>
### #####修改列
先试一下看看能不能不拷贝表,
(mysql5.-)root@localhost [test]> alter table ddl_test modify column address varchar() not null default '',ALGORITHM=INPLACE, lock=none;
ERROR (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
呵呵了,要拷贝表
(mysql5.-)root@localhost [test]> alter table ddl_test modify column address varchar() not null default '',ALGORITHM=copy, lock=none;
那么看看是不是不锁表,也呵呵了,只支持共享锁,那么意味这阻塞DML,不阻塞select
ERROR (0A000): LOCK=NONE is not supported. Reason: COPY algorithm requires a lock. Try LOCK=SHARED.
(mysql5.-)root@localhost [test]> 小结:只要LOCK模式不是NONE的DDL操作,锁最小范围都是共享锁,意味着还是会阻塞DML,其他情况我就不一一测试了,参考官方手册
问题
#### (mysql5.-)root@localhost [test]> alter table ddl_test add column address varchar() not null default '',ALGORITHM=INPLACE, lock=none;
ERROR (HY000): Creating index 'FTS_DOC_ID_INDEX' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again.
(mysql5.-)root@localhost [test]> show global variables like 'innodb_online_alter_log_max_size';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| innodb_online_alter_log_max_size | |
+----------------------------------+-----------+
row in set (0.02 sec)
支持动态修改
(mysql5.-)root@localhost [test]>
(mysql5.-)root@localhost [(none)]> set global innodb_online_alter_log_max_size=;
Query OK, rows affected (0.00 sec) (mysql5.-)root@localhost [(none)]> innodb存储引擎实现Online DDL的原理是在执行创建或者删除操作同时,将DML操作日志写入到一个缓存中,待完成后再将重做应用到表上,以此达到数据的一致性。
这个缓存的大小由参数innodb_online_alter_log_max_size控制,默认大小为128MB,支持动态修改
如果更新的表比较大,并且创建过程中有大量的写操作,如果遇到innodb_online_alter_log_max_size的空间不能存放日志时,会抛出相应的错误,
如果遇到改错误,我们可以调大该参数,以此获得更大的日志缓存空间。还可以设置lock的模式为SHARE,这样在执行过程中会阻塞写操作发生,
因此不需要进行DML日志的记录。
参考资料:
http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html