pt-online-schema-change在线修改表结构

1、背景

MySQL大字段的DDL操做:加减字段、索引、修改字段属性等,在5.1以前都是很是耗时耗力的,特别是会对MySQL服务产生影响。在5.1以后随着Plugin Innodb的出如今线加索引的提升了不少,可是还会影响(时间缩短了),主要是在更改期间,会生成一个互斥锁,阻塞对整个表的全部操做。不过MySQL 5.6能够避免上面的状况,支持在线DDL操做了。但目前大部分在用的版本都是5.6以前的,因此DDL操做一直是运维人员头疼的事。那如何在不锁表的状况下安全快速地更新表结构?如今来讲明下percona-toolkit的pt-online-schema-change(简称:OSC)的使用说明,能够很好的解决上述的问题。php

在咱们的之前作法中,为了避免影响线上业务,咱们通常采用:先在线下从库更改表结构,而后替换线上从库,这样一台台的修改,最后作一下主库切换,这个过程会耗费很长时间,而且在作主库切换时,风险也很是的大,咱们怎样才能让时间更短,且能不阻塞读写状况下在线修改呢?早在2008年Shlomi Noach 就利用触发器的原理,开发了python版本oak-online-alter-table在线更改表结构脚本,最近,Percona公司在本身的percona-toolkit脚本集合中也发布了在线更改表结构的perl版本脚本pt-online-schema-change,Facebook公司也开发本身的在线更改表结构php版本脚本OnlineSchemaChange.php,而它们最底层的实现原理都为触发器。由于oak-online-alter-table不肯定是否在被开发与支持,而OnlineSchemaChange.php的使用比较复杂,且有不少功能不支持如:表结构中若是有外键的状况,故在此我就Percona公司的脚本作详细的剖析。html

2、OSC详细实现原理及过程

pt-online-schema-change在线更改表结构的实现核心有以下几个过程:(注:在跟改过程当中涉及到三个表:原表、tmp_table即做为原表导数据的临时表,old_table在最后rename原表的结果表)python

第一步:新建tmp_table,表结构同原表。mysql

 

1sql

CREATE TABLE $db.$tmp_tbl LIKE $db.$tbl数据库

第二步:在tmp_table上更改表结构为须要的表结构。安全

 

1服务器

ALTER TABLE $db.$tmp_tbl ADD $col $type多线程

第三步:在原表上建立三个触发器,以便后续原表上的操做同步到新表。app

 

1

2

3

CREATE TRIGGER `pt_osc_test_online_table_del` AFTER DELETE ON `test`.`online_table` FOR EACH ROW DELETE IGNORE FROM `test`.`_online_table_new` WHERE `test`.`_online_table_new`.`id` <=> OLD.`id`

CREATE TRIGGER `pt_osc_test_online_table_upd` AFTER UPDATE ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`)

CREATE TRIGGER `pt_osc_test_online_table_ins` AFTER INSERT ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`)

建立触发器,用于记录从拷贝数据开始以后,对源数据表继续进行数据修改的操做记录下来,用于数据拷贝结束后,执行这些操做,保证数据不会丢失。若是表中已经定义了触发器这个工具就不能工做了。咱们能够看到这三个触发器分别对应于INSERT、UPDATE、DELETE三种操做:

1) pt_osc_test_online_table_del,DELETE操做,咱们注意到DELETE IGNORE,当新有数据时,咱们才进行操做,也就是说,当在后续导入过程当中,若是删除的这个数据还未导入到新表,那么咱们能够不在新表执行操做,由于在之后的导入过程当中,原表中改行数据已经被删除,已经没有数据,那么他也就不会导入到新表中。

2) pt_osc_test_online_table_ins,INSERT操做,全部的INSERT INTO所有转换为REPLACE INTO,为了确保数据的一致性,当有新数据插入到原表时,若是触发器还未把原表数据未同步到新表,这条数据已经被导入到新表了,那么咱们就能够利用replace into进行覆盖,这样数据也是一致的。

3) pt_osc_test_online_table_upd,UPDATE操做,全部的UPDATE也转换为REPLACE INTO,由于当新的数据的行还未同步到新表时,新表是不存在这条记录的,那么咱们就只能插入该条数据,若是已经同步到新表了,那么也能够进行覆盖插入,全部数据与原表也是一致的。

咱们也能看出上述的精髓也就这这几条replace into操做,正是由于这几条replace into才能保证数据的一致性。

第四步:拷贝原表数据到新表,数据量大时会根据主键进行分段chunk插入。

 

1

INSERT LOW_PRIORITY IGNORE INTO `test`.`_tt_new` (`id`, `name`) SELECT `id`, `name` FROM `test`.`tt` LOCK IN SHARE MODE /*pt-online-schema-change 21745 copy table*/

数据的拷贝是基于小块(根据chunk-time参数指定)进行的,并且根据主键或者索引进行选择,因此对总体服务器性能影响较小。它是经过一些查询(基本为主键、惟一键值)分批把数据导入到新的表中,在导入前,咱们能经过参数 --chunk-size 对每次导入行数进行控制,已减小对原表的锁定时间,而且在导入时,咱们能经过 --sleep 参数控制,在每一个chunk导入后与下一次chunk导入开始前sleep一会,sleep时间越长,对于磁盘IO的冲击就越小。

第五步:交换表,其经过一个RENAME TABLE同时处理两个表,实现原子操做。

 

1

RENAME TABLE `test`.`online_table` TO `test`.`_online_table_old`, `test`.`_online_table_new` TO `test`.`online_table`

其经过一个RENAME TABLE同时处理两个表,实现原子操做。在rename过程,其实咱们仍是会致使写入读取堵塞的,因此从严格意思上说,咱们的OSC也不是对线上环境没有一点影响,但因为rename操做只是一个修更名字的过程,也只会修改一些表的信息,基本是瞬间结束,故对线上影响不太大。

第六步:清理以上过程当中的old表,触发器。

 

1

2

3

4

DROP TABLE IF EXISTS `test`.`_tt_old`

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_del`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_upd`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_ins`;

总结

以上即为整个Percona OSC的过程,咱们看到精华部分就触发器那一块,不过还有不少细节未介绍,如:外键、记录binlog等等,因为环境的复杂性,此工具仍是有不少风险,如如下几个方面问题或者须要规避的一些问题:

1)此工具不是原子操做,若是某一点失败,不只仅会留下不少中间过程的垃圾文件,而这些文件很难彻底清理,而且若是有这些文件存在,那么就不能在次执行OSC操做。

2)在执行时,尽可能避免有这个表的批量更新、锁表、优化表的操做,咱们能想象的到,若是有锁表、优化表那么OSC是否还能正常执行?

3)若是存在主从结构,那么尽可能在从库先执行。由于若是在主库执行完毕后再到从库执行,咱们能想象,主库字段多,此时数据同步到从库,会不会有问题呢?

4)必须有单一列的主键或者单一惟一键,这样咱们在insert select * from分片时,是否是能更好的处理量呢?

5)不要有外键,尽管脚本通过严格测试,可是是否还有bug,也未知,表的外键是否是会带来更多的问题呢?

6)在执行以前,咱们是否是要对磁盘容量进行评估呢?由于OSC会使用表的一倍以上空间。

以上列到的,只是部分问题,我想若是须要在线进行实施,还须要通过严格的测试,可是它的实现为咱们提供了一个很好的在线更改表结构方法,我相信只要咱们能很好的规避他的弊端,它会给咱们带来很大的帮助,对于MySQL 5.6以前的数据库版本,在MySQL 5.6版本开始已经支持Online DDL操做了。

3、OSC使用语法

具体安装能够参考:Percona Toolkit工具集介绍

 

 

1

$ pt-online-schema-change [OPTIONS] DSN

经常使用选项说明:

--user,-u

#链接的用户名

--password,-p

#链接的密码

--database,-D

#链接的数据库

--port,-P

#链接数据库的端口

--host,-h

#链接的主机地址

--socket,-S

#链接的套接字文件

--ask-pass

#隐式输入链接MySQL的密码

--charset

#指定修改的字符集

--defaults-file,-F

#读取配置文件

--alter

#结构变动语句,不须要alter table关键字,能够指定多个更改,用逗号分隔。以下场景,须要注意:

1)不能用RENAME来重命名表。

2)列不能经过先删除,再添加的方式进行重命名,这会将数据拷贝到新列。

3)若是加入的列非空并且没有默认值,则工具会失败,其不会为你设置一个默认值,必须显示指定。

4)删除外键(drop foreign key constrain_name)时,须要指定名称_constraint_name,而不是原始的constraint_name。如:CONSTRAINT `fk_foo` FOREIGN KEY (`foo_id`) REFERENCES `bar` (`foo_id`),须要指定:–alter “DROP FOREIGN KEY _fk_foo”

--alter-foreign-keys-method

#如何把外键引用到新表?须要特殊处理带有外键约束的表,以保证它们能够应用到新表。当重命名表的时候,外键关系会带到重命名后的表上。该工具备两种方法,能够自动找到子表,并修改约束关系。

1)auto:在rebuild_constraints和drop_swap两种处理方式中选择一个。

2)rebuild_constraints:使用ALTER TABLE语句先删除外键约束,而后再添加。若是子表很大的话,会致使长时间的阻塞。

3)drop_swap: 执行FOREIGN_KEY_CHECKS=0,禁止外键约束,删除原表,再重命名新表。这种方式很快,也不会产生阻塞,可是有风险:

3.1 在删除原表和重命名新表的短期内,表是不存在的,程序会返回错误。

3.2 若是重命名表出现错误,也不能回滚了,由于原表已经被删除。

4)none: 相似”drop_swap”的处理方式,可是它不删除原表,而且外键关系会随着重命名转到老表上面。

--[no]check-alter

#默认yes,语法解析。配合 --dry-run 和 --print 一块儿运行,来检查是否有问题(change column,drop primary key)。

--max-lag

#默认1s,每一个chunk拷贝完成后,会查看全部复制Slave的延迟状况。要是延迟大于该值,则暂停复制数据,直到全部从的滞后小于这个值,使用Seconds_Behind_Master。若是有任何从滞后超过此选项的值,则该工具将睡眠 --check-interval 指定的时间,再检查。若是从被中止,将会永远等待,直到从开始同步,而且延迟小于该值。若是指定 --check-slave-lag ,该工具只检查该服务器的延迟,而不是全部服务器。

--check-slave-lag

#指定一个从库的DSN链接地址,若是从库超过 --max-lag 参数设置的值,就会暂停操做。

--recursion-method = none

#默认是show processlist,发现从的方法,也能够是host,但须要在从上指定report_host,经过show slave hosts来找到,能够指定none来不检查Slave的主从延迟。

--check-interval

#默认是1。 --max-lag 检查的睡眠时间。

--[no]check-plan

#默认yes,检查查询执行计划的安全性。

--[no]check-replication-filters

#默认yes,若是工具检测到服务器选项中有任何复制相关的筛选,如指定binlog_ignore_db和replicate_do_db此类。发现有这样的筛选,工具会报错且退出。由于若是更新的表Master上存在,而Slave上不存在,会致使复制的失败。使用 --no-check-replication-filters 选项来禁用该检查。

--[no]swap-tables

#默认yes,交换原始表和新表,除非你禁止 --[no]drop-old-table 。

--[no]drop-triggers

#默认yes,删除原表上的触发器。 --no-drop-triggers 会强制开启 --no-drop-old-table ,即不删除触发器就会强制不删除原表。

--new-table-name

#复制建立新表的名称,默认%T_new。

--[no]drop-new-table

#默认yes。删除新表,若是复制组织表失败。

--[no]drop-old-table

#默认yes。复制数据完成重命名以后,删除原表。若是有错误则会保留原表。

--max-load

#默认为Threads_running=25。每一个chunk拷贝完后,会检查SHOW GLOBAL STATUS的内容,检查指标是否超过了指定的阈值。若是超过,则先暂停。这里能够用逗号分隔,指定多个条件,每一个条件格式:status指标=MAX_VALUE或者status指标:MAX_VALUE。若是不指定MAX_VALUE,那么工具会这只其为当前值的120%。

--critical-load

#默认为Threads_running=50。用法基本与 --max-load 相似,若是不指定MAX_VALUE,那么工具会这只其为当前值的200%。若是超过指定值,则工具直接退出,而不是暂停。

--default-engine

#默认状况下,新的表与原始表是相同的存储引擎,因此若是原来的表使用InnoDB的,那么新表将使用InnoDB的。在涉及复制某些状况下,极可能主从的存储引擎不同。使用该选项会默认使用默认的存储引擎。

--set-vars

#设置MySQL变量,多个用逗号分割。默认该工具设置的是:wait_timeout=10000、innodb_lock_wait_timeout=一、lock_wait_timeout=60。

--chunk-size-limit

#当须要复制的块远大于设置的chunk-size大小,就不复制,默认值是4.0,一个没有主键或惟一索引的表,块大小就是不肯定的。

--chunk-time

#在chunk-time执行的时间内,动态调整chunk-size的大小,以适应服务器性能的变化,该参数设置为0。或者指定chunk-size,均可以禁止动态调整。

--chunk-size

#指定块的大小,默认是1000行,能够添加k,M,G后缀。这个块的大小要尽可能与 --chunk-time 匹配,若是明确指定这个选项,那么每一个块就会指定行数的大小。

--[no]check-plan

#默认yes。为了安全,查查询的执行计划,默认状况下,这个工具在执行查询以前会先EXPLAIN,以获取一次少许的数据。若是是很差的EXPLAIN,那么会获取一次大量的数据,这个工具会屡次执行EXPALIN,若是EXPLAIN不一样的结果,那么就会认为这个查询是不安全的。

--statistics

#打印出内部事件的数目,能够看到复制数据插入的数目。

--dry-run

#建立和修改新表,但不会建立触发器、复制数据、和替换原表。并不真正执行,能够看到生成的执行语句,了解其执行步骤与细节。 --dry-run 与 --execute 必须指定一个,两者相互排斥。和–print配合最佳。

--execute

#肯定修改表,则指定该参数会真正开始执行。 --dry-run与--execute 必须指定一个,两者相互排斥。

--print

#打印SQL语句到标准输出。指定此选项可让你看到该工具所执行的语句,和 --dry-run 配合最佳。

--progress

#复制数据的时候打印进度报告,二部分组成:第一部分是百分比,第二部分是时间。

--quiet,-q

#不把信息标准输出。

4、OSC使用实战

测试一:简单使用

第一步:建立一个测试表,表引擎为myisam

 

1

2

3

4

5

CREATE TABLE `online_table` (

  `id` int(11) NOT NULL auto_increment primary key (id),

  `name` varchar(10) DEFAULT NULL,

  `age` int(11) DEFAULT NULL

) engine = myisam default charset utf8;

PS:使用OSC时,原表须要一个主键或惟一索引,由于删除的触发器须要,不然数据不会被复制。上面已经说明了该工具的实现方式,下面来执行看是否有效。

第二步:更改存储引擎查看OSC实现方式和原理

 

1

2

3

4

5

6

7

$ pt-online-schema-change \

--user=root \

--password=zabbixzabbix \

--host=127.0.0.1 \

--alter "engine=innodb" D=test,t=online_table \

--print \

--dry-run

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

Operation, tries, wait:

  analyze_table, 10, 1

  copy_rows, 10, 0.25

  create_triggers, 10, 1

  drop_triggers, 10, 1

  swap_tables, 10, 1

  update_foreign_keys, 10, 1

Starting a dry run.  `test`.`online_table` will not be altered.  Specify --execute instead of --dry-run to alter the table.

Creating new table...    \\第一步,建立一个新表;

CREATE TABLE `test`.`_online_table_new` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `name` varchar(10) DEFAULT NULL,

  `age` int(11) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8

Created new table test._online_table_new OK.

Altering new table...    \\第二步,更改表结构;

ALTER TABLE `test`.`_online_table_new` engine=innodb

Altered `test`.`_online_table_new` OK.

Not creating triggers because this is a dry run.    \\建立触发器,这里因为使用--dry-run因此并不会建立;

CREATE TRIGGER `pt_osc_test_online_table_del` AFTER DELETE ON `test`.`online_table` FOR EACH ROW DELETE IGNORE FROM `test`.`_online_table_new` WHERE `test`.`_online_table_new`.`id` <=> OLD.`id`

CREATE TRIGGER `pt_osc_test_online_table_upd` AFTER UPDATE ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`)

CREATE TRIGGER `pt_osc_test_online_table_ins` AFTER INSERT ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`)

Not copying rows because this is a dry run.

INSERT LOW_PRIORITY IGNORE INTO `test`.`_online_table_new` (`id`, `name`, `age`) SELECT `id`, `name`, `age` FROM `test`.`online_table` LOCK IN SHARE MODE /*pt-online-schema-change 21023 copy table*/

Not swapping tables because this is a dry run.

Not dropping old table because this is a dry run.

Not dropping triggers because this is a dry run.

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_del`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_upd`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_ins`;

2016-05-16T14:54:27 Dropping new table...

DROP TABLE IF EXISTS `test`.`_online_table_new`;

2016-05-16T14:54:27 Dropped new table OK.

Dry run complete.  `test`.`online_table` was not altered.

若是表没有主键或惟一键,会出现以下这一句:

 

1

2

The new table `test`.`_online_table_new` does not have a PRIMARY KEY or a unique index which is required for the DELETE trigger.

//原表须要一个主键或则惟一索引,由于删除的触发器须要,不然数据不会被复制;

第三步:使用–execute正式修改表的存储引擎(DDL操做)

 

1

2

3

4

5

6

7

$ pt-online-schema-change \

--user=root \

--password=zabbixzabbix \

--host=127.0.0.1 \

--alter "engine=innodb" D=test,t=online_table \

--execute \

--print

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

No slaves found.  See --recursion-method if host www.ywnds.com has slaves.

Not checking slave lag because no slaves were found and --check-slave-lag was not specified.

Operation, tries, wait:

  analyze_table, 10, 1

  copy_rows, 10, 0.25

  create_triggers, 10, 1

  drop_triggers, 10, 1

  swap_tables, 10, 1

  update_foreign_keys, 10, 1

Altering `test`.`online_table`...

Creating new table...    //建立新表;

CREATE TABLE `test`.`_online_table_new` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `name` varchar(10) DEFAULT NULL,

  `age` int(11) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8

Created new table test._online_table_new OK.

Altering new table...    //更改表;

ALTER TABLE `test`.`_online_table_new` engine=innodb

Altered `test`.`_online_table_new` OK.

2016-05-16T14:04:12 Creating triggers...    //建立触发器;

CREATE TRIGGER `pt_osc_test_online_table_del` AFTER DELETE ON `test`.`online_table` FOR EACH ROW DELETE IGNORE FROM `test`.`_online_table_new` WHERE `test`.`_online_table_new`.`id` <=> OLD.`id`

CREATE TRIGGER `pt_osc_test_online_table_upd` AFTER UPDATE ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`)

CREATE TRIGGER `pt_osc_test_online_table_ins` AFTER INSERT ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`)

2016-05-16T14:04:12 Created triggers OK.

2016-05-16T14:04:12 Copying approximately 0 rows...    //执行数据复制的操做,原表数据插入到新表;

INSERT LOW_PRIORITY IGNORE INTO `test`.`_online_table_new` (`id`, `name`, `age`) SELECT `id`, `name`, `age` FROM `test`.`online_table` LOCK IN SHARE MODE /*pt-online-schema-change 20603 copy table*/

2016-05-16T14:04:12 Copied rows OK.

2016-05-16T14:04:12 Swapping tables...        //插入完毕后交换表;

RENAME TABLE `test`.`online_table` TO `test`.`_online_table_old`, `test`.`_online_table_new` TO `test`.`online_table`

2016-05-16T14:04:12 Swapped original and new tables OK.

2016-05-16T14:04:12 Dropping old table...     //删除旧表;

DROP TABLE IF EXISTS `test`.`_online_table_old`    

2016-05-16T14:04:12 Dropped old table `test`.`_online_table_old` OK.

2016-05-16T14:04:12 Dropping triggers...      //删除触发器;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_del`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_upd`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_ins`;

2016-05-16T14:04:12 Dropped triggers OK.

Successfully altered `test`.`online_table`.

上面指定了 --execute ,输出的信息里面也表示原表已经被修改为功而且记录了很详细的操做信息,查看表的引擎是否已经被修改:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

mysql> show table status like 'online_table'\G

*************************** 1. row ***************************

           Name: online_table

         Engine: InnoDB

        Version: 10

     Row_format: Compact

           Rows: 0

Avg_row_length: 0

    Data_length: 16384

Max_data_length: 0

   Index_length: 0

      Data_free: 9437184

Auto_increment: 1

    Create_time: 2016-05-16 15:46:40

    Update_time: NULL

     Check_time: NULL

      Collation: utf8_general_ci

       Checksum: NULL

Create_options:

        Comment:

1 row in set (0.00 sec)

测试二:主从环境

在主从环境下,修改表结构时尽可能在从库先执行,由于若是先在主库执行完毕后再到从库执行,咱们能想象,主库字段多,数据同步到从库,会不会有问题呢,要考虑?

另外在主从环境下,若是添加一个字段须要加上 --no-check-replication-filters 选项,语句以下。

 

1

2

3

4

5

6

7

8

$ pt-online-schema-change \

--user=root \

--password=zabbixzabbix \

--host=127.0.0.1 \

--alter "ADD COLUMN content text" D=test,t=online_table \

--execute \

--print \

--no-check-replication-filters

若是不加 --no-check-replication-filters 选项的话,那么就会报错的,由于默认会检查主从。除了add column,也能够modify column,drop column,对于change column则须要指定: --no-check-alter 参数。

测试三:主外键表的基本操做

 

1

2

3

4

5

6

7

$ pt-online-schema-change \

--user=root \

--password=zabbixzabbix \

--host=127.0.0.1 \

--alter "ADD COLUMN content text" D=test,t=tt \

--execute \

--print

 

 

1

2

3

4

5

6

7

8

9

10

11

12

No slaves found.  See --recursion-method if host www.ywnds.com has slaves.

Not checking slave lag because no slaves were found and --check-slave-lag was not specified.

Operation, tries, wait:

  analyze_table, 10, 1

  copy_rows, 10, 0.25

  create_triggers, 10, 1

  drop_triggers, 10, 1

  swap_tables, 10, 1

  update_foreign_keys, 10, 1

Child tables:

  `test`.`xx` (approx. 1 rows)

You did not specify --alter-foreign-keys-method, but there are foreign keys that reference the table. Please read the tool's documentation carefully.

执行错误退出,提示须要指定: --alter-foreign-keys-method 参数来操做有外键的表。要是没有外键而加了参数的话会出现:

No foreign keys reference `aaa`.`xx`; ignoring –alter-foreign-keys-method。

 

1

2

3

4

5

6

7

8

$ pt-online-schema-change \

--user=root \

--password=zabbixzabbix \

--host=127.0.0.1 \

--alter "ADD COLUMN content text" D=test,t=tt \

--execute \

--print \

--alter-foreign-keys-method auto

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

No slaves found.  See --recursion-method if host www.ywnds.com has slaves.

Not checking slave lag because no slaves were found and --check-slave-lag was not specified.

Operation, tries, wait:

  analyze_table, 10, 1

  copy_rows, 10, 0.25

  create_triggers, 10, 1

  drop_triggers, 10, 1

  swap_tables, 10, 1

  update_foreign_keys, 10, 1

Child tables:    //子表

  `test`.`xx` (approx. 1 rows)

Will automatically choose the method to update foreign keys.

Altering `test`.`tt`...

Creating new table...   //建立新表

CREATE TABLE `test`.`_tt_new` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `name` varchar(10) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

Created new table test._tt_new OK.

Altering new table...   //修改新表

ALTER TABLE `test`.`_tt_new` ADD COLUMN content text

Altered `test`.`_tt_new` OK.

2016-05-16T16:39:30 Creating triggers...    //建立触发器

CREATE TRIGGER `pt_osc_test_tt_del` AFTER DELETE ON `test`.`tt` FOR EACH ROW DELETE IGNORE FROM `test`.`_tt_new` WHERE `test`.`_tt_new`.`id` <=> OLD.`id`

CREATE TRIGGER `pt_osc_test_tt_upd` AFTER UPDATE ON `test`.`tt` FOR EACH ROW REPLACE INTO `test`.`_tt_new` (`id`, `name`) VALUES (NEW.`id`, NEW.`name`)

CREATE TRIGGER `pt_osc_test_tt_ins` AFTER INSERT ON `test`.`tt` FOR EACH ROW REPLACE INTO `test`.`_tt_new` (`id`, `name`) VALUES (NEW.`id`, NEW.`name`)

2016-05-16T16:39:30 Created triggers OK.

2016-05-16T16:39:30 Copying approximately 1 rows...    //复制数据

INSERT LOW_PRIORITY IGNORE INTO `test`.`_tt_new` (`id`, `name`) SELECT `id`, `name` FROM `test`.`tt` LOCK IN SHARE MODE /*pt-online-schema-change 21745 copy table*/

2016-05-16T16:39:30 Copied rows OK.

2016-05-16T16:39:30 Max rows for the rebuild_constraints method: 4000

Determining the method to update foreign keys...     //处理外键的方式,选择的是auto,会根据状况进行选择:重建或则禁用外键检测。

2016-05-16T16:39:30   `test`.`xx`: 1 rows; can use rebuild_constraints

2016-05-16T16:39:30 Swapping tables...        //交换表

RENAME TABLE `test`.`tt` TO `test`.`_tt_old`, `test`.`_tt_new` TO `test`.`tt`

2016-05-16T16:39:31 Swapped original and new tables OK.

2016-05-16T16:39:31 Rebuilding foreign key constraints...   //重建外键

ALTER TABLE `test`.`xx` DROP FOREIGN KEY `xx_ibfk_1`, ADD CONSTRAINT `_xx_ibfk_1` FOREIGN KEY (`tt_id`) REFERENCES `test`.`tt` (`id`)

2016-05-16T16:39:31 Rebuilt foreign key constraints OK.  

2016-05-16T16:39:31 Dropping old table...

DROP TABLE IF EXISTS `test`.`_tt_old`

2016-05-16T16:39:31 Dropped old table `test`.`_tt_old` OK.

2016-05-16T16:39:31 Dropping triggers...

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_del`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_upd`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_ins`;

2016-05-16T16:39:31 Dropped triggers OK.

Successfully altered `test`.`tt`.

对可靠性要求不高能够用auto模式更新,要是可靠性要求高则须要用rebuild_constraints模式。即:

 

1

--alter-foreign-keys-method=rebuild_constraints

总结

1)上面的测试都是把原表删除了,要是不删除原表则: --no-drop-old-table ,这样会让原表(_test_binlog_old)保留。

2)要是在线上环境上添加字段,但又不想影响到服务,能够用参数: --max-load 去执行该工具,默认是Threads_running=25,即当前有这么多线程在运行的时候就暂停数据的复制,等少于该值则继续复制数据到新表:

3)当业务量较大时,修改操做会等待没有数据修改后,执行最后的rename操做。所以,在修改表结构时,应该尽可能选择在业务相对空闲时,至少修改表上的数据操做较低时,执行较为稳当。

4)若是对外键表操做时,四种外键操做类型须要根据表的数据量和可靠程度,进行选择。处于可靠性的缘由,尽可能使用rebuild_constraints类型,若是没有可靠性要求,可使用auto类型。

5)因为可能存在必定的风险,在操做以前,建议对数据表进行备份,可使得操做更安全、可靠。

使用该工具的前提是处理的表须要有主键或则惟一索引。当处理有外键的表时,须要加 --alter-foreign-keys-method 参数,值能够根据状况设置。当是主从环境,不在意从的延迟,则须要加 --recursion-method=none 参数。当须要尽量的对服务产生小的影响,则须要加上 --max-load 参数。

相关文章
相关标签/搜索