最近经常在线上经常遇到有性能问题的SQL,有些表没有合理添加索引,有些表添加的索引不合理,各种各样的问题,导致SQL的执行效率不高。这时DBA们不得不重构SQL,使其达到最好的性能,这时我们往往要在线添加或者删除索引、字段等等的操作。如果是MySQL 5.5的版本在DDL方面是要付出代价的,虽然已经有了Fast index Creation,但是在添加字段还是会锁表的,而且在添加删除辅助索引是会加S锁,也就是无法进行写操作。所以,这里就有相关工具的出现,那就是pt-online-schema-change和oak-online-alter-table,都实现了Online DDL,但是每个工具都有相应自己的限制。如果是MySQL5.6以及更高的版本,已经开始支持Online DDL,我们下面的研究,针对MySQL5.6之前的版本。

主要介绍两个工具:oak-online-alter-table & pt-online-schema-change

一、oak-online-alter-table安装及使用

openark工具包是一组用于MySQL的实用工具,该工具集解决日常维护任务,这些工作比较复杂和耗时。其中oak-online-alter-table就是该工具集中的一个工具,该工具执行非阻塞ALTER TABLE的操作。当然还有其他的工具,有关openark工具说明可以参考http://code.openark.org/forge/openark-kit

1)安装openark工具包(安装依赖)

[root@xuanzhi-mysql ~]# yum install python-mysqldb MySQL-python -y

2)openark软件包下载:(一般只有海外服务器才可以直接在服务器上下载,下载个包相信难不了大家的)

[root@xuanzhi-mysql ~]# wget https://openarkkit.googlecode.com/files/openark-kit-196-1.noarch.rpm

3)openark的安装:

[root@xuanzhi-mysql ~]# rpm -ivh openark-kit--.noarch.rpm
Preparing... ########################################### [%]
:openark-kit ########################################### [%]
[root@xuanzhi-mysql ~]#

 该工具提供了以下三种基本功能:

1、一个非阻塞ALTER TABLE操作,以下几种情况都是支持的

1)添加列 (新列必须有一个默认值)

2)删除列 (旧表必须有一个单列的唯一索引)

3)修改列 (改变字段类型,包括唯一键的列)

4)添加索引 (普通索引,唯一索引,全文索引)

5)删除索引(旧表必须有一个单列的唯一索引)

6) 修改表引擎:当处理非事务性引擎应该格外注意

7)添加外键约束

2、(可能会在未来版本不再支持):创建一个镜像表,与原始表同步,只要不发生如下操作:

1)对原始表ALTER TABLE操作

2)对原始表TRUNCATE操作

3)使用LOAD DATA INFILE向原始表导入数据

4)对原始表OPTIMIZE TABLE操作

3、一个空的ALTER,重建一个表:释放磁盘空间和重新组织表,相当于优化表。

oak-online-alter-table的一些使用限制进行说明
1、在该表上面至少有一个单列的UNIQUE KEY

2、更改原始表为单个字段的唯一索引

3、该表没有定义触发器“AFTER”(oak会自己创建触发器)

4、应该没有外键约束(FOREIGN KEY)

5、表名长度不超过57个字符

oak-online-alter-table的工作原理

该工具运行时,它允许INSERT,UPDATE,DELETE,REPLACE原始表,但是不允许TRUNCATE,ALTER,REPAIR OPTIMIZE或者其他方式对原表进行操作。该工具适用于InnoDB表,MyISAM表,或以其他任何表级锁的存储引擎(MEMORY, ARCHIVE)。该工具工作原理是创建一个镜像表的同时,它慢慢与原始表同步。直到同步完成,要做到这一点,该工具必须在原始表创建AFTER INSERT, AFTER UPDATE, AFTER DELETE触发器。镜像表与原始表同步发生在几个步骤。在这些步骤中,数据被从原始表复制到镜像表。这是以行块进行,这个大小是可以用chunk-size选项配置的。当一个块被复制,在(MyISAM,ARCHIVE,MEMORY)存储引擎上有读锁,或包含在该块上面的行记录(innodb),较小的块——更快的锁被移除,允许更大的并发性。对于写密集型应用,它可能是可取的,允许对块之间的停顿,以使尽可能减少影响。这可以使用sleep-ratio选项进行配置。而块之间停顿时没有被加锁。即便如此,对性能的影响是在运行应用程序时,这是由于触发器被添加到表上和DML语句在向镜像表同步。它需要有足够的磁盘空间来容纳改变的表(如一个正常的ALTER TABLE)。在操作完成时才出现磁盘空间恢复(取决于你的存储引擎和配置)。

测试如下:

1、添加一个字段

[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5./mysql.sock --table=sakila.film --alter="ADD COLUMN name VARCHAR(64) DEFAULT ''"
-- Connecting to MySQL
-- Table sakila.film is of engine innodb
-- ERROR: Errors found. Initiating cleanup
-- Tables unlocked
-- ERROR: Table must not have any 'AFTER' triggers defined.
[root@xuanzhi-mysql ~]#

很明显提示有触发器,也是上面提到的使用限制。

[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5./mysql.sock --table=sakila.actor --alter="ADD COLUMN name VARCHAR(64) DEFAULT ''"
-- Connecting to MySQL
-- Table sakila.actor is of engine innodb
-- ERROR: Errors found. Initiating cleanup
-- Tables unlocked
-- ERROR: Table must not have any foreign keys defined (neither as parent nor child).

提示了有外键约束,无论是父表还是子表 ,如果有外键的话,都是不允许添加字段的。

下面给t1表添加一个辅助索引看看

mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) DEFAULT NULL,
`name` char(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) mysql>

在name字段添加普通索引:

[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5./mysql.sock --table=sakila.t1 --alter="ADD KEY(name)"
-- Connecting to MySQL
-- Table sakila.t1 is of engine innodb
-- Checking for UNIQUE columns on sakila.t1, by which to chunk
-- Possible UNIQUE KEY column names in sakila.t1:
-- ERROR: Errors found. Initiating cleanup
-- Tables unlocked
-- ERROR: Table must have a UNIQUE KEY on a single column
[root@xuanzhi-mysql ~]#

可以看到提示说没有唯一键,也是上面提及到的使用限制。我们添加一个唯一键再看看:

mysql> alter table t1 add unique key (id);
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0 mysql>
[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5./mysql.sock --table=sakila.t1 --alter="ADD KEY(name)"
-- Connecting to MySQL
-- Table sakila.t1 is of engine innodb
-- Checking for UNIQUE columns on sakila.t1, by which to chunk
-- Possible UNIQUE KEY column names in sakila.t1:
-- - id
-- Table sakila.__oak_t1 has been created
-- Table sakila.__oak_t1 has been altered
-- Checking for UNIQUE columns on sakila.__oak_t1, by which to chunk
-- Possible UNIQUE KEY column names in sakila.__oak_t1:
-- - id
-- Checking for UNIQUE columns on sakila.t1, by which to chunk
-- - Found following possible unique keys:
-- - id (int)
-- Chosen unique key is 'id'
-- Shared columns: id, name
-- Created AD trigger
-- Created AU trigger
-- Created AI trigger
-- Attempting to lock tables -- Tables locked WRITE
/usr/local/bin/oak-online-alter-table:: Warning: No data - zero rows fetched, selected, or processed
num_affected_rows = cursor.execute(query)
-- id (min, max) values: ([None], [None])
-- Tables unlocked
-- Table sakila.t1 has been renamed to sakila.__arc_t1,
-- and table sakila.__oak_t1 has been renamed to sakila.t1
-- Table sakila.__arc_t1 was found and dropped
-- ALTER TABLE completed
[root@xuanzhi-mysql ~]#

可以看到已经添加成功了,我们查看下表结构,是否真的成功了:(上面的输出有一个警告,不用理会,是因为我是空表,没有记录)

mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) DEFAULT NULL,
`name` char(20) DEFAULT NULL,
UNIQUE KEY `id` (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) mysql>

在添加字段时如果设置了NOT NULL,但没有给默认值,也会报警告

[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5.40/mysql.sock --table=sakila.t1 --alter="ADD COLUMN sex VARCHAR(64) not null"
-- Connecting to MySQL
-- Table sakila.t1 is of engine innodb
-- Checking for UNIQUE columns on sakila.t1, by which to chunk
-- Possible UNIQUE KEY column names in sakila.t1:
-- - id
-- Table sakila.__oak_t1 has been created
-- Table sakila.__oak_t1 has been altered
-- Checking for UNIQUE columns on sakila.__oak_t1, by which to chunk
-- Possible UNIQUE KEY column names in sakila.__oak_t1:
-- - id
-- Checking for UNIQUE columns on sakila.t1, by which to chunk
-- - Found following possible unique keys:
-- - id (int)
-- Chosen unique key is 'id'
-- Shared columns: salarey, id, name
-- Created AD trigger
-- Created AU trigger
-- Created AI trigger
-- Attempting to lock tables -- Tables locked WRITE
-- id (min, max) values: ([1L], [1L])
-- Tables unlocked
-- - Reminder: altering sakila.t1: ADD COLUMN sex VARCHAR(64) not...
-- Copying range (1), (1), progress: 100%
/usr/local/bin/oak-online-alter-table:84: Warning: Field 'sex' doesn't have a default value
num_affected_rows = cursor.execute(query)
-- Copying range 100% complete. Number of rows: 1
-- - Reminder: altering sakila.t1: ADD COLUMN sex VARCHAR(64) not...
-- Deleting range (1), (1), progress: 100%
-- Deleting range 100% complete. Number of rows: 0
-- Table sakila.t1 has been renamed to sakila.__arc_t1,
-- and table sakila.__oak_t1 has been renamed to sakila.t1
-- Table sakila.__arc_t1 was found and dropped
-- ALTER TABLE completed
[root@xuanzhi-mysql ~]#
查看表结构,还是添加成功了的。
mysql> desc t1;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int(11) | YES | UNI | NULL | |
| name | char(20) | YES | MUL | NULL | |
| sex | varchar(64) | NO | | NULL | |
+---------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec) mysql>

oak-online-alter-table还有更多的操作,同学们自己测试哈。

下面测试一下各种操作是否会锁表,这也是我们最关心的问题。

1、我们直接在mysql终端直接执行添加字段看看有什么情况:

mysql> show processlist;
+----+------+-----------+------------+---------+------+---------------------------------+------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------------+---------+------+---------------------------------+------------------------------------------------------------+
| 25 | root | localhost | distribute | Query | 53 | copy to tmp table | alter table test1 add age int(3) |
| 26 | root | localhost | distribute | Query | 28 | Waiting for table metadata lock | insert into test1(name,code) values ('xuanhi','aa102') |
| 29 | root | localhost | distribute | Query | 0 | NULL | show processlist |
+----+------+-----------+------------+---------+------+---------------------------------+------------------------------------------------------------+
3 rows in set (0.00 sec)

相信大家都看到,在添加表字段的时候,会把整个表锁住,所以看到session2窗口插入数据时,要等待释放锁。

2、我们再测试上添加普通索引看看会不会锁表:

可以看到session2窗口也一样要等待的。如果对一个很大的表直接进行添加或者删除字段和索引的时候,会阻塞所有对该表的DML操作的,所以在线上谨慎直接添加或者删除字段和索引

我们用oak-online-alter-table

[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5./mysql.sock --table=distribute.test1 --alter="ADD COLUMN address VARCHAR(64)"
.....
......
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- - Reminder: altering distribute.test1: ADD COLUMN address VARCHAR()...
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range % complete. Number of rows:
-- Table distribute.test1 has been renamed to distribute.__arc_test1,
-- and table distribute.__oak_test1 has been renamed to distribute.test1
-- Table distribute.__arc_test1 was found and dropped
-- ALTER TABLE completed

在seesion2进行DML操作:

mysql> delete from test1 where name="xuanhi4";
Query OK, 1 row affected (2.69 sec) mysql> insert into test1(name,code) values ('xuanhi4','aa1333');
Query OK, 1 row affected (0.04 sec) mysql> select * from test1 where code='aa1333';
+---------+---------+--------+
| id | name | code |
+---------+---------+--------+
| 7014150 | xuanhi4 | aa1333 |
+---------+---------+--------+
1 row in set (1.12 sec) mysql> update test1 set name="xuanhi5" where id=7014150;
Query OK, 1 row affected (0.11 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql>

可以看到执行成功的返回时间都是很少的,执行操作的时候并没有锁等待。

我们添加索引看看,是否要锁等待:

[root@xuanzhi-mysql ~]# oak-online-alter-table -uroot -p123456 -S /data/mysql-5.5./mysql.sock --table=distribute.test1 --alter="ADD KEY (name,age)"
...
....
......
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- - Reminder: altering distribute.test1: ADD KEY (name,age)...
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range (), (), progress: %
-- Deleting range % complete. Number of rows:
-- Table distribute.test1 has been renamed to distribute.__arc_test1,
-- and table distribute.__oak_test1 has been renamed to distribute.test1
-- Table distribute.__arc_test1 was found and dropped
-- ALTER TABLE completed

show processlist 查看是否有锁表:

mysql> show processlist;
+----+------+-----------+------------+---------+------+--------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------------+---------+------+--------------+------------------------------------------------------------------------------------------------------+
| 25 | root | localhost | distribute | Sleep | 544 | | NULL |
| 26 | root | localhost | distribute | Query | 1 | NULL | show processlist |
| 29 | root | localhost | distribute | Sleep | 1260 | | NULL |
| 31 | root | localhost | distribute | Query | 1 | Sending data | INSERT IGNORE INTO distribute.__oak_test1 (`age`, `code`, `id`, `name`, `address`)
(SELE |
+----+------+-----------+------------+---------+------+--------------+------------------------------------------------------------------------------------------------------+
4 rows in set (0.45 sec) mysql>

可以看到使用工具在线添加索引和字段都不会锁表,有兴趣的朋友,可以测试删除字段和索引的,其实一样的,我这里就不测试了。mysql5.6虽然支持了Online DDL,但mysql 5.6 Online DDL的时候测试过如果在执行alter table之前已经有一个慢查询或者结果集比较大的查询,那么此时执行ALTER TABLE是会导致锁表的,用oak-online-alter-table也是一样的要等待锁的释放,本人已经测试过。(mysql5.6 online ddl的博客下次有空会补上)

mysql> show processlist;
+----+------+-----------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
| 25 | root | localhost | distribute | Query | 0 | NULL | show processlist |
| 26 | root | localhost | distribute | Sleep | 961 | | NULL |
| 29 | root | localhost | distribute | Query | 19 | Sending data | select * from test1 |
| 32 | root | localhost | distribute | Query | 6 | Waiting for table metadata lock | CREATE TRIGGER distribute.test1_AD_oak AFTER DELETE ON distribute.test1
FOR EACH ROW
|
+----+------+-----------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
4 rows in set (0.25 sec) mysql>

oak-online-alter-table工具的使用,暂时说到这里,有兴趣的朋友可以参考官网

http://openarkkit.googlecode.com/svn/trunk/openarkkit/doc/html/oak-online-alter-table.html(自备梯子)

二、pt-online-schema-change

该工具是percona-toolkit工具包中其中的一个工具,简单说和oak-online-alter-table有着一样的功能。都是实现在线架构改变的工具。其他的我就不多说了。重点说工作原理和注意事项。

(1)安装依赖包:

[root@xuanzhi-mysql ~]# yum install perl-IO-Socket-SSL perl-DBD-MySQL perl-Time-HiRes -y

(2)下载软件:

[root@xuanzhi-mysql ~]# wget http://www.percona.com/downloads/percona-toolkit/LATEST/RPM/percona-toolkit-2.2.8-1.noarch.rpm

(3)安装软件

[root@xuanzhi-mysql ~]# rpm -ivh percona-toolkit-2.2.-.noarch.rpm
warning: percona-toolkit-2.2.-.noarch.rpm: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY
Preparing... ########################################### [%]
:percona-toolkit ########################################### [%]
[root@xuanzhi-mysql ~]#

大概工作原理如下:

(1)如果存在外键,根据alter-foreign-keys-method参数的值,检测外键相关的表,针对相应的设置进行处理。

(2)创建一个新的表,表结构为修改后的数据表,用于从源数据表向新表中导入数据。

(3)创建触发器,在复制数据开始之后,将对源数据表继续进行数据修改的操作记录下来,以便在数据复制结束后执行这些操作,保证数据不会丢失。

(4)复制数据,从源数据表中复制数据到新表中。

(5)修改外键相关的子表,根据修改后的数据,修改外键关联的子表。

(6)更改源数据表为old表,把新表改为源表名,并将old表删除。

(7)删除触发器。

存在如下限制:

(1)对操作的表必须要有主键或者唯一键

(2)增加的字段如果为NOT NULL,会报错,需要添加默认值才可以成功。

测试:

使用方法:

[root@xuanzhi-mysql ~]# pt-online-schema-change
Usage: pt-online-schema-change [OPTIONS] DSN

测试表test2结构如下:

mysql> show create table test2\G
*************************** 1. row ***************************
Table: test2
Create Table: CREATE TABLE `test2` (
`id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) mysql>

给表test2添加一个字段:

[root@xuanzhi-mysql ~]# pt-online-schema-change --alter="add column name char(20)" --user=root --password= --socket=/data/mysql-5.5./mysql.sock  D=sakila,t=test2 --execute
No slaves found. See --recursion-method if host redis-master has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
copy_rows, , 0.25
create_triggers, ,
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `sakila`.`test2`...
Creating new table...
Created new table sakila._test2_new OK.
Altering new table...
Altered `sakila`.`_test2_new` OK.
--27T22:: Dropping new table...
--27T22:: Dropped new table OK.
`sakila`.`test2` was not altered.
The new table `sakila`.`_test2_new` does not have a PRIMARY KEY or a unique index which is required for the DELETE trigger.
[root@xuanzhi-mysql ~]#

可以发现提示表没有主键或者唯一键,所以添加失败。添加主键以后再进行测试。

mysql> alter table test2 add primary key (id);
Query OK, rows affected (0.04 sec)
Records: Duplicates: Warnings: mysql>
[root@xuanzhi-mysql ~]# pt-online-schema-change --alter="add column name char(20)" --user=root --password= --socket=/data/mysql-5.5./mysql.sock  D=sakila,t=test2 --execute
No slaves found. See --recursion-method if host redis-master has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
copy_rows, , 0.25
create_triggers, ,
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `sakila`.`test2`...
Creating new table...
Created new table sakila._test2_new OK.
Altering new table...
Altered `sakila`.`_test2_new` OK.
--27T22:: Creating triggers...
--27T22:: Created triggers OK.
--27T22:: Copying approximately rows...
--27T22:: Copied rows OK.
--27T22:: Swapping tables...
--27T22:: Swapped original and new tables OK.
--27T22:: Dropping old table...
--27T22:: Dropped old table `sakila`.`_test2_old` OK.
--27T22:: Dropping triggers...
--27T22:: Dropped triggers OK.
Successfully altered `sakila`.`test2`.
[root@xuanzhi-mysql ~]#

可以看到添加了主键后,就成功添加了。但是设置NOT NULL,但是不给默认值,看看神马情况

[root@xuanzhi-mysql ~]# pt-online-schema-change --alter="add column last_name char(20) not null" --user=root --password= --socket=/data/mysql-5.5./mysql.sock  D=sakila,t=test2 --execute
No slaves found. See --recursion-method if host redis-master has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
copy_rows, , 0.25
create_triggers, ,
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `sakila`.`test2`...
Creating new table...
Created new table sakila._test2_new OK.
Altering new table...
Altered `sakila`.`_test2_new` OK.
--27T23:: Creating triggers...
--27T23:: Created triggers OK.
--27T23:: Copying approximately rows...
--27T23:: Dropping triggers...
--27T23:: Dropped triggers OK.
--27T23:: Dropping new table...
--27T23:: Dropped new table OK.
`sakila`.`test2` was not altered.
--27T23:: Error copying rows from `sakila`.`test2` to `sakila`.`_test2_new`: --27T23:: Copying rows caused a MySQL error :
Level: Warning
Code:
Message: Field 'last_name' doesn't have a default value
Query: INSERT LOW_PRIORITY IGNORE INTO `sakila`.`_test2_new` (`id`, `age`, `name`) SELECT `id`, `age`, `name` FROM `sakila`.`test2` LOCK IN SHARE MODE /*pt-online-schema-change 4369 copy table*/ [root@xuanzhi-mysql ~]#

可以看到没有给默认值的情况下,添加字段失败,下面给一个默认值再看看:

[root@xuanzhi-mysql ~]# pt-online-schema-change --alter="add column last_name char(20) not null default 'xuanzhi'" --user=root --password= --socket=/data/mysql-5.5./mysql.sock  D=sakila,t=test2 --execute
No slaves found. See --recursion-method if host redis-master has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
copy_rows, , 0.25
create_triggers, ,
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `sakila`.`test2`...
Creating new table...
Created new table sakila._test2_new OK.
Altering new table...
Altered `sakila`.`_test2_new` OK.
--27T23:: Creating triggers...
--27T23:: Created triggers OK.
--27T23:: Copying approximately rows...
--27T23:: Copied rows OK.
--27T23:: Swapping tables...
--27T23:: Swapped original and new tables OK.
--27T23:: Dropping old table...
--27T23:: Dropped old table `sakila`.`_test2_old` OK.
--27T23:: Dropping triggers...
--27T23:: Dropped triggers OK.
Successfully altered `sakila`.`test2`.
[root@xuanzhi-mysql ~]#

可以看见已经成功了,相信更多的小伙伴想看看对大表添加字段和索引会不会锁表,莫急,下面我们一起来道道:

下面对一个有六百多万行数据的测试表进行添加字段看是否会锁表:(如果想自己造数据的同学可以用sysbench生成的1000w数据)

mysql> show create table xuanzhi\G
*************************** 1. row ***************************
Table: xuanzhi
Create Table: CREATE TABLE `xuanzhi` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(32) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code_index` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7014157 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) mysql> select count(*) from xuanzhi;
+----------+
| count(*) |
+----------+
| 6862437 |
+----------+
1 row in set (0.03 sec) mysql>

测试1、在线添加字段并查看是否有阻塞DML操作

[root@xuanzhi-mysql ~]# pt-online-schema-change --alter="add column name char(20) not null default 'man'" --user=root --password=123456 --socket=/data/mysl-5.5.40/mysql.sock  D=sakila,t=xuanzhi --execute                                 
Operation, tries, wait:
copy_rows, 10, 0.25
create_triggers, 10, 1
drop_triggers, 10, 1
swap_tables, 10, 1
update_foreign_keys, 10, 1
Altering `sakila`.`xuanzhi`...
Creating new table...
Created new table sakila._xuanzhi_new OK.
Altering new table...
Altered `sakila`.`_xuanzhi_new` OK.
2015-05-28T01:38:44 Creating triggers...
2015-05-28T01:38:44 Created triggers OK.
2015-05-28T01:38:44 Copying approximately 6862437 rows...
Copying `sakila`.`xuanzhi`: 29% 01:13 remain
Copying `sakila`.`xuanzhi`: 51% 00:55 remain
Copying `sakila`.`xuanzhi`: 66% 00:45 remain
Copying `sakila`.`xuanzhi`: 73% 00:43 remain
Copying `sakila`.`xuanzhi`: 78% 00:40 remain
Copying `sakila`.`xuanzhi`: 85% 00:31 remain
Copying `sakila`.`xuanzhi`: 91% 00:20 remain
Copying `sakila`.`xuanzhi`: 95% 00:10 remain
2015-05-28T01:43:07 Copied rows OK.
2015-05-28T01:43:07 Swapping tables...
2015-05-28T01:43:21 Swapped original and new tables OK.
2015-05-28T01:43:21 Dropping old table...
2015-05-28T01:43:21 Dropped old table `sakila`.`_xuanzhi_old` OK.
2015-05-28T01:43:21 Dropping triggers...
2015-05-28T01:43:21 Dropped triggers OK.
Successfully altered `sakila`.`xuanzhi`.
[root@xuanzhi-mysql ~]#

可以看到并没有阻塞DML操作,上面执行成功的返回时间有点久,可以忽略,因为在虚拟机测试生成的报告,所以同学们懂的!!

测试2:在线添加索引,看是否阻塞DML操作

[root@xuanzhi-mysql ~]# pt-online-schema-change --alter="ADD KEY (name)" --user=root --password= --socket=/data/mysql-5.5./mysql.sock  D=sakila,t=xuanzhi --execute
No slaves found. See --recursion-method if host redis-master has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
copy_rows, , 0.25
create_triggers, ,
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `sakila`.`xuanzhi`...
Creating new table...
Created new table sakila._xuanzhi_new OK.
Altering new table...
Altered `sakila`.`_xuanzhi_new` OK.
--28T01:: Creating triggers...
--28T01:: Created triggers OK.
--28T01:: Copying approximately rows...
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
Copying `sakila`.`xuanzhi`: % : remain
--28T02:: Copied rows OK.
--28T02:: Swapping tables...
--28T02:: Swapped original and new tables OK.
--28T02:: Dropping old table...
--28T02:: Dropped old table `sakila`.`_xuanzhi_old` OK.
--28T02:: Dropping triggers...
--28T02:: Dropped triggers OK.
Successfully altered `sakila`.`xuanzhi`.
[root@xuanzhi-mysql ~]#

相信大家都看到了吧,在线添加索引,对DML操作是木有阻塞的。最后查看一下表结构:

mysql> show create table xuanzhi\G
*************************** 1. row ***************************
Table: xuanzhi
Create Table: CREATE TABLE `xuanzhi` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(32) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`name` char(20) NOT NULL DEFAULT 'man',
PRIMARY KEY (`id`),
UNIQUE KEY `code_index` (`code`) USING BTREE,
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=7014161 DEFAULT CHARSET=utf8
1 row in set (0.08 sec) mysql>

成功添加了name字段,成功的在列name上添加了普通索引。

测试3:测试一下在执行alter table之前有一个大的查询,看是否导致锁等待,这个在MySQL 5.6以及oak-online-alter-table都有这个问题

在session1窗口:

mysql> select * from xuanzhi;

在session2和session3窗口查看:

并没有发现锁表的情况,但在网上看到的资料是会锁表的,希望同学们也自己测试下,大家一起学习哈!!

总结:

一、如果要在线上进行Online DDL操作,一定要做好数据备份,要考虑数据的大小情况及主从框架中如果从是提供读的,要考虑到延时等问题。

二、pt-online-schema-change比oak-online-alter-table工具更好用,并且存在的限制较少,oak-online-alter-table对有外键的表是没有办法的

三、已经执行了大的查询,这时恰好执行ALTER TABLE操作,都会导致锁表,MySQL5.6版本也有这样的情况。

四、MySQL5.6版本已经支持Online DDL了,这是一个不错的改进。如果对一个大数据表DDL,一般选择避开业务高峰期执行,所以还是要在业务量较低且没有大查询时执行Online DDL

参考资料:

https://openarkkit.googlecode.com/svn/trunk/openarkkit/doc/html/oak-online-alter-table.html(自备梯子)

https://www.percona.com/doc/percona-toolkit/2.1/pt-online-schema-change.html

http://www.cnblogs.com/gomysql/p/3777607.html

作者:陆炫志

出处:xuanzhi的博客 http://www.cnblogs.com/xuanzhi201111

您的支持是对博主最大的鼓励,感谢您的认真阅读。本文版权归作者所有,欢迎转载,但请保留该声明。

 
 

Online DDL工具的安装与使用的更多相关文章

  1. MySQL在线DDL工具 gh-ost

    一.简介 gh-ost基于 golang 语言,是 github 开源的一个 DDL 工具,是 GitHub's Online Schema Transmogrifier/Transfigurator ...

  2. 微软源代码管理工具TFS2013安装与使用详细图文教程(Vs2013)

    这篇文章联合软件小编主要介绍了微软源代码管理工具TFS2013安装与使用图文教程,本文详细的给出了TFS2013的安装配置过程.使用教程,需要的朋友可以参考下 最近公司新开发一个项目要用微软的TFS2 ...

  3. [转] 微软源代码管理工具TFS2013安装与使用详细图文教程(Vs2013)

    这篇文章联合软件小编主要介绍了微软源代码管理工具TFS2013安装与使用图文教程,本文详细的给出了TFS2013的安装配置过程.使用教程,需要的朋友可以参考下 最近公司新开发一个项目要用微软的TFS2 ...

  4. nmon工具的安装及简单使用

    1.工具的安装 下载rpm包安装即可http://mirror.ghettoforge.org/distributions/gf/el/6/gf/x86_64/nmon-14i-1.gf.el6.x8 ...

  5. PHP性能优化工具–xhprof安装

    PHP性能优化工具–xhprof安装,这里我先贴出大致的步骤: 1.获取xhprof 2.编译前预处理 3.编译安装 4.配置php.ini 5.查看运行结果 那么下面我们开始安装xhprof工具吧: ...

  6. [资料收集]MySQL在线DDL工具pt-online-schema-change

    MySQL在线DDL工具pt-online-schema-change pt-online-schema-change使用说明(未完待续) 官网

  7. PHP 代码质量检测工具的安装与使用

    代码统计工具 PHPLOC安装:wget https://phar.phpunit.de/phploc.phar chmod +x phploc.phar sudo mv phploc.phar /u ...

  8. Hadoop集群中pig工具的安装过程记录

    在Hadoop环境中安装了pig工具,安装过程中碰到了一些问题,在此做一下记录:   主要安装流程参考:http://www.cnblogs.com/yanghuahui/p/3768270.html ...

  9. 第一章:绪论-Python开发工具的安装

    书中提到了操作系统平台尽量选 *nix.我这里选用的是 ubuntu 14.04 , 下面的操作均以此操作系统为例说明. 操作系统安装教程可以去网站上找,推荐用虚拟机的方式,Windows下可用的虚拟 ...

随机推荐

  1. 【BZOJ1205】[HNOI2005]星际贸易(动态规划)

    [BZOJ1205][HNOI2005]星际贸易(动态规划) 题面 BZOJ 洛谷 题解 第一问就是一个裸\(dp\),因为什么都不用考虑... 所以设\(f[i][j]\)表示当前停靠在第\(i\) ...

  2. vi基础学习总结

    标签(空格分隔): vi 总结 vi是几乎所有类Unix/Linux系统下都默认装有的常用文本编辑工具.本文记录初学vi的一些小知识. 0.界面模式 在命令行使用"vi"编辑文档时 ...

  3. POJ 2135 Farm Tour (网络流,最小费用最大流)

    POJ 2135 Farm Tour (网络流,最小费用最大流) Description When FJ's friends visit him on the farm, he likes to sh ...

  4. 用HashSet存储不重复的对象

    直接进入主题,先来创建一个类:Bean public class Bean { private int id; private String name; public Bean() { super() ...

  5. OpenStack 图形化服务 Horizon使用(十三)

    构建一台云主机 上图中Count可以选择同时创建多台 最终“启动实例” 创建成功后,可以进入控制台,操作新建云主机

  6. C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

    前提:一维数组和一维指针为什么可以替换使用? ] = { , , }; int *p = a; ; i < ; i++) printf("%d ", *(p + i)); 上 ...

  7. bzoj千题计划273:bzoj4710: [Jsoi2011]分特产

    http://www.lydsy.com/JudgeOnline/problem.php?id=4710 答案=总方案数-不合法方案数 f[i][j] 前i种特产分给j个人(可能有人没有分到特产)的总 ...

  8. HDU 5299 圆扫描线 + 树上删边

    几何+博弈的简单组合技 给出n个圆,有包含关系,以这个关系做游戏,每次操作可以选择把一个圆及它内部的圆全部删除,不能操作者输. 圆的包含关系显然可以看做是树型结构,所以也就是树上删边的游戏. 而找圆的 ...

  9. Linux遇到的问题(一)Ubuntu报“xxx is not in the sudoers file.This incident will be reported” 错误解决方法

    提示错误信息 www@iZ236j3sofdZ:~$ ifconfig Command 'ifconfig' is available in '/sbin/ifconfig' The command ...

  10. 《Linux命令行与shell脚本编程大全》23章24章

    第二十三章 使用其他shell bash shell是linux发行版中最广泛使用的shell.但是它并不是唯一的选择,还有其他的shell可以供你选择. 23.1 什么是dash shell 百度百 ...