使用pt-online-schema-change工具修改schema时,会先创建一个与原数据表拥有相同结构的新表,然后将原表中的数据逐步复制到新表。
例如一个拥有id,name数据列的zs表,向该表添加名为uid新列时,使用如下命令:
root@localhost:mysql3316.sock 14:24:33 [test]>show create table zsG *************************** 1. row *************************** Table: zs Create Table: CREATE TABLE `zs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 1 row in set (0.00 sec) root@localhost:mysql3316.sock 14:27:10 [test]>SELECT * FROM zs; +----+-----------+ | id | name | +----+-----------+ | 1 | zhangshuo | +----+-----------+ 1 row in set (0.00 sec)
[root@bogon data]# pt-online-schema-change --alter "add uid int" D=test,t=zs --no-drop-old-table --no-drop-new-table --chunk-size=500 --chunk-size-limit=600 --defaults-file=/usr/local/mysql/my3316.cnf --host=localhost --port=3316 --charset=utf8 --user=root --ask-pass --progress=time,30 --max-load="threads_running=100" --critical-load="threads_running=1000" --chunk-index=PRIMARY --execute
1.pt-online-schema-change处理上述命令时,会 先根据原zs数据表的结构创建新表,(_zs_new),并将要添加的列加到新表:
58 Query SHOW VARIABLES LIKE 'wsrep_on' 58 Query /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */ 58 Query USE `test` 58 Query SHOW CREATE TABLE `test`.`zs` 58 Query /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */ 58 Query CREATE TABLE `test`.`_zs_new` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 58 Query ALTER TABLE `test`.`_zs_new` add uid int 58 Query /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */ 58 Query USE `test` 58 Query SHOW CREATE TABLE `test`.`_zs_new` 58 Query /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */
2.然后创建after触发器,使原数据表(zs)的insert、update、delete操作内容能传递到_zs_new数据表:
58 Query CREATE TRIGGER `pt_osc_test_zs_del` AFTER DELETE ON `test`.`zs` FOR EACH ROW DELETE IGNORE FROM `test`.`_zs_new` WHERE `test`.`_zs_new`.`id` <=> OLD.`id` 58 Query CREATE TRIGGER `pt_osc_test_zs_upd` AFTER UPDATE ON `test`.`zs` FOR EACH ROW REPLACE INTO `test`.`_zs_new` (`id`, `name`) VALUES (NEW.`id`, NEW.`name`) 58 Query CREATE TRIGGER `pt_osc_test_zs_ins` AFTER INSERT ON `test`.`zs` FOR EACH ROW REPLACE INTO `test`.`_zs_new` (`id`, `name`) VALUES (NEW.`id`, NEW.`name`)
3.pt-online-schema-change读取原数据表中的记录,所读条数由chunk-size选项确定,将读取的记录复制到_zs_new数据表。
58 Query EXPLAIN SELECT * FROM `test`.`zs` WHERE 1=1 58 Query EXPLAIN SELECT `id`, `name` FROM `test`.`zs` LOCK IN SHARE MODE /*explain pt-online-schema-change 13294 copy table*/ 58 Query INSERT LOW_PRIORITY IGNORE INTO `test`.`_zs_new` (`id`, `name`) SELECT `id`, `name` FROM `test`.`zs` LOCK IN SHARE MODE /*pt-online-schema-change 13294 copy table*/
4.用户对原zs数据表执行的DML都会通过触发器自动反映到_zs_new数据表。pt-online-schema-change复制完所有记录后,使用rename将zs表更名为_zs_old,将_zs_new表名修改为zs。rename命令会将多个数据表的更名操作作为一个事物进行处理,所以更名过程中用户的查询不会失败。
58 Query SHOW GLOBAL STATUS LIKE 'threads_running' 58 Query ANALYZE TABLE `test`.`_zs_new` /* pt-online-schema-change */ 58 Query RENAME TABLE `test`.`zs` TO `test`.`_zs_old`, `test`.`_zs_new` TO `test`.`zs` 58 Query DROP TRIGGER IF EXISTS `test`.`pt_osc_test_zs_del` 58 Query DROP TRIGGER IF EXISTS `test`.`pt_osc_test_zs_upd` 58 Query DROP TRIGGER IF EXISTS `test`.`pt_osc_test_zs_ins` 58 Query SHOW TABLES FROM `test` LIKE '\_zs\_new'
5.使用pt-online-schema-change应注意
(1)重复触发器:如果原数据表已经存在AFTER触发器,将无法使用pt-online-schema-change。
(2)死锁:假设zs数据表的id列有唯一索引(unique index),pt-online-schema-change会使用主键,以内存为单位分割记录,然后将所选范围内的记录复制到_zs_new数据表。这一过程中对_zs_new数据表的主键加排它锁后,为了进行重复检查,还要对id列唯一索引加锁。用户修改的内容(insert、delete、update)也通过触发器应用到_zs_new数据表。但是通过触发器到来的查询会先对id列中创建的唯一索引加排它锁,然后试图再次向主键加锁时,会与pt-online-schema-change进程的以内存块为单位的复制作业产生死锁。