【用途】在线改表

【注意风险】因为涉及到修改表的数据和结构,所以在使用前要小心测试并做好备份,工具默认不会改表,除非你添加了--execute参数

【工具简介】

pt-osc模仿MySQL内部的改表方式进行改表,但整个改表过程是通过对原始表的拷贝来完成的,即在改表过程中原始表不会被锁定,并不影响对该表的读写操作。

首先,osc创建与原始表相同的不包含数据的新表并按照需求进行表结构的修改,然后将原始表中的数据按chunk大小逐步拷贝到新表中,当拷贝完成后,会自动同时修改原始表和新表的名字并默认将原始表删除

这个过程中有两个问题需要注意:

1. 触发器

因为整个过程是在线的,为了将改表过程中对原始表的更新同时更新到新表上,会创建相应的触发器,每当发生针对原始表的增删改操作,就会触发对新表的相应的操作。所以原始表上不能有其他触发器,即如果原始表上存有触发器,OSC会罢工的

2. 外键

外键使改表操作变得更加复杂,如果原始表上有外键的话,自动rename原始表和新表的操作就不能顺利进行,必须要在数据拷贝完成后将外键更新到新表上,该工具有两种方法来支持这个操作,具体后面参数部分(--alter-foreign-keys-method)介绍

【使用示例】

pt-online-schema-change [OPTIONS] DSN

添加一个列:
pt-online-schema-change --alter "ADD COLUMN c1 INT" D=sakila,t=actor OPTIMIZE TABLE造成的负载较大,可以改用OSC来执行:
pt-online-schema-change --alter "ENGINE=InnoDB" D=sakila,t=actor

安全性保障措施

1. 默认如果检测到有复制过滤会拒绝改表,修改参数为--[no]check-replication-filters

2. 默认如果检测到主从复制延迟会自动停止数据拷贝,调节参数为--max-lag

3. 默认如果检测到服务器负载过重会停止或中断操作,调节参数为--max-load和--critical-load

4. 默认会设置锁等待超时时间为1s来避免干扰其他事务的进行,调节参数为--lock-wait-timeout

5. 默认如果检测到外键冲突后会拒绝改表,调节参数为--alter-foreign-keys-method

6. 该工具不能在PXC(Percona XtraDB Cluster)集群中对myisam表进行改表操作

对PXC的支持

支持PXC5.5.28-23.7或者更新的版本,但是有两个限制,如果不符合任一条件就会报错退出:

1. 只支持innodb表的改表

2. wsrep_OSU_method必须被设置为TOI (total order isolation)

参数详细介绍

1. --alter

用于指定改表的操作,可以同时指定多个操作,应用如下:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=sakila,t=actor

注意:

(1)改表名时不可用RENAME(压根就不要用OSC来改表名,直接rename多好)

(2)修改列名时不可以通过drop该列然后重新add该列来完成,这样会导致无法拷贝原始列的数据出来

(3)如果add一个列没设默认值且设置为‘not null’,工具会拒绝执行,因为工具不会帮你猜一个默认值出来

(4)drop外键时,外键名字前需要加 ‘_’ 而不是单纯的就一个外键名字,如drop下面这个外键时

CONSTRAINT `fk_foo` FOREIGN KEY (`foo_id`) REFERENCES `bar` (`foo_id`)

  必须这样指定:

--alter "DROP FOREIGN KEY _fk_foo"

(5)MySQL版本为5.0时需要注意,该工具不能使用LOCK IN SHARE MODE,这会引起一个复制错误,报错如下:

Query caused different errors on master and slave. Error on master:
'Deadlock found when trying to get lock; try restarting transaction' (1213),
Error on slave: 'no error' (0). Default database: 'pt_osc'.
Query: 'INSERT INTO pt_osc.t (id, c) VALUES ('730', 'new row')'

  一般发生在转换myisam表为innodb表时,因为myisam表不支持事务而innodb表支持。5.1及更新版本没有该问题

2. --alter-foreign-keys-method

外键改表前后必须持续的链接正确的表,当该工具rename原始表并用新表来取代原始表时,外键必须正确更新到新表上,并且原始表中的外键不再生效

有两种方法来实现这个目的,具体参数有四:

(1)auto

自动决定采用哪个方法,如果可以就采用rebuild_constraints,如果不可以就采用drop_swap

(2)rebuild_constraints

该方法采用alter table来drop并re-add链接新表的外键。除非相关的子表太大使得alter过程花费时间过长,一般都采用该方法。这里的花费时间是通过比较子表中的行数和该工具将原始表数据拷贝到新表中的拷贝速率来评估的,如果评估后发现子表中数据能够在少于--chunk-time的时间内alter完成,就会采用该方法。另外,因为在MySQL中alter table比外部拷贝数据的速率快很多,所以拷贝速率是按照--chunk-size-limit来决定的

因为MySQL的限制,外键在改表前后的名字会不一样,改表后新表中的外键名前会加一个下划线,同样,会自动的更改外键相应的索引名字

(3)drop_swap

该方法禁止外键检查(FOREIGN_KEY_CHECKS=0),然后在rename新表之前就将原始表drop掉,这个方法更快而且不会被阻塞,但是风险比较大,风险有二:

♥ 在drop掉原始表和rename新表之间有一个时间差,在这段时间里这个表是不存在的,这会导致查询报错

♥ 如果rename新表时发生了错误,那问题就大了,因为原始表已经被drop掉了,只能呵呵了

(4)none

这个方法类似没有“swap”的drop_swap,原始表中的所有外键都会被指定到一个不存在的表上,这样会导致查询show engine innodb status时会显示如下信息:

Trying to add to index `idx_fk_staff_id` tuple:
DATA TUPLE: 2 fields;
0: len 1; hex 05; asc ;;
1: len 4; hex 80000001; asc ;;
But the parent table `sakila`.`staff_old`
or its .ibd file does not currently exist!

因为原始表(database.tablename)会被rename为database.tablename_old然后drop掉。这种处理外键的方法可以让DBA在需要时取消该工具的这种内置功能

3. --charset

默认字符类型,例如如果值为utf8,就将输出的字符设置为utf8格式,将mysql_enable_utf8传递给DBD::mysql,然后连接MySQL后运行SET NAMES UTF8命令

4.  --[no]check-alter

用于改表安全警告,目前支持两种报警:

(1)Column renames

该工具以前的版本中,用CHANGE COLUMN name new_name来改变列名时会导致该列的数据丢失,后来的版本中修补了这个bug,但是这段代码仍然不够万无一失,所以在操作前最好用--dry-run 和 --print去检测一下确定可以正确的rename column

(2)DROP PRIMARY KEY

除非指定--dry-run,否则一旦执行DROP PRIMARY KEY就会发出警告。该工具的触发器,尤其是delete trigger,主要是l利用主键来执行触发的,所以会受到很大的影响。所以最好先用--dry-run 和 --print去检测一下确保trigger可以正确执行

5. --[no]check-replication-filters

如果主从复制中有复制过滤就报错退出,即如果检测到有binlog_ignore_db、replicate_do_db等过滤手段,就报错退出

6. --check-slave-lag

如果复制延迟大于--max-lag就停止数据拷贝,设置该参数后会监控所有的复制进程,一旦发现任何大于--max-lag时间的就停止数据拷贝,如果不想监控所有的复制进程,可以通过--recursion-method来指定想要监控的对象

7. --recursion-method

master寻找slave的方法,默认值为processlist,hosts,所有方法如下:

METHOD       USES
=========== ==================
processlist SHOW PROCESSLIST
hosts SHOW SLAVE HOSTS
dsn=DSN DSNs from a table
none Do not find slaves

(1)processlist是默认的,因为show slave status不太靠谱。

(2)如果服务器使用非3306端口时,hosts方法也很好

(3)dsn=DSN方法使用时,需要先去库里创建一个表,比如在percona库中建一个dnsn表

建表语句是:

CREATE TABLE `dsns` (`id` int(11) NOT NULL AUTO_INCREMENT,`parent_id` int(11) DEFAULT NULL,`dsn` varchar(255) NOT NULL,PRIMARY KEY (`id`));

建好后插入主从复制信息数据,如:insert into table dsns(dsn) values(h=slave_host,u=repl_user,p=repl_password,P=port );

然后就可以使用DSN方法了:命令为:--recursion-method dsn=D=percona,t=dsns.

(4)如果想只监控hosts10.10.1.16 and 10.10.1.17的复制延迟情况,可以将h=10.10.1.16 and h=10.10.1.17的相关内容存入‘dsns’的表中来精确指定

8. --chunk-time

默认是0.5秒,工具会根据当前系统运行繁忙程度计算出在该指定时间内可以处理的数据行数(即chunk),比较灵活

9. --dry-run

create并alter新表,但不创建trigger,不拷贝数据以及替换原始表

10. --execute

该参数用于执行alter操作,如果不加的话,只会做一些安全检查后退出。一定要确保知道如何使用该工具并有合适的备份后,再添加该参数。使用该参数时,除了对对象表所需的权限外,还需要SUPER, REPLICATION SLAVE两种权限。

11. --max-lag(type: time; default: 1s)

中断数据拷贝直到所有的复制延迟都少于这个值,默认为1S。每一个chunk拷贝完成后,OSC都会去show salve status通过Seconds_Behind_Master来确定所有的复制情况,任何相关的slave的复制延迟高于该值时,OSC就会停止数据拷贝--check-interval参数所指定的时间,然后重新发起检查,直到延迟降低到该值以下。

如果指定了--check-slave-lag参数,OSC则只检查指定的slave延迟情况,而不是所有的slave。如果想精确的指定监控服务器,可以用--recursion-method的DSN去指定

12. --max-load(type: Array; default: Threads_running=25)

该参数的目的是为了防止OSC执行过程造成的负载太大,如果数据拷贝引起了锁等待或者负载过大,其他查询会被阻塞并形成队列,这会引起Threads_running增长。OSC会在每次chunk拷贝完成后执行show global status来检测系统状态,如果设置了一些系统参数的临界值,一旦检测到超过临界值,osc就停止数据拷贝直到检测值回到临界值以下。如果这样还是发现有阻塞,那最好把chunk time值调小

在每个chunk拷贝后都执行show global status进行检查,如果发现状态参数高于它们的临界值就中断拷贝,该参数接受一个以逗号分隔的MySQL状态参数list,参数赋值方式为variable_name=max_value或variable_name:max_value,如果不指定,OSC会将参数值定为当前参数值的120%

即:如果指定Threads_connected参数,不赋值且当前该参数的值是100,则Threads_connected超过120时就会中断数据拷贝,如果想精确指定的话,就用‘=’或‘:’去精确赋值

【其他参数】

1. --ask-pass  

Prompt for a password when connecting to MySQL

2. --check-interval(type: time; default: 1)

Sleep time between checks for --max-lag.

3. --chunk-index

用于指定索引

4. --chunk-index-columns

用于指定联合索引的采用列

5. --chunk-size

用于指定chunk的大小,不推荐使用,建议用--chunk-time

6. --chunk-size-limit

用于限制chunk的大小

7. --config

指定配置,如果要用该参数,必须放在命令行的最前面

8. --critical-load

类似于--max-load,不同的是检测到超高负载时会直接中断OSC进程而不是暂停

9. --default-engine

指定引擎,采用才参数后会使新表采用MySQL当前的默认存储引擎而不是原始表的

10. --defaults-file

指定配置文件,必须用绝对路径

11. --[no]drop-new-table

默认在拷贝失败后会将新表drop掉,可以用该参数配置

12. --[no]drop-old-table

默认在改表结束后会将原始表drop掉,可以用该参数配置

13. --host,--password,--pid,--port,--socket,--user

连接MySQL数据库的各参数

14. --print

将改表过程中的SQL语句输出到STDOUT,观察改表过程中的操作时用这个参数配合--dry-run是极好的

15. --progress

在拷贝数据过程中将进程报告输出到STDERR,参数的指定由两部分组成,第一部分是percentage, time, or iterations,第二部分是分别对应钱第一部分的数值指定,即百分值,秒数和次数,默认是time,30

16. --quiet

不要输出信息到STDOUT,错误和警告信息仍会输出到STDERR

17. --recurse

复制层级的指定,默认是无限层级

18. --retries

当数据拷贝过程中有非致命性错误,如锁等待超时或查询被kill等情况时,重新尝试的次数。默认是3

19. --set-vars

用set ........直接将该指定参数中的值在MySQL客户端中执行

20. --[no]swap-tables

默认在改表结束后用新表来替换原始表,可以用该参数配置

【DSN】

指定时注意大小写敏感,“=”左右不能有空格,多个值之间用逗号分隔

1. A               charset

2. D               database

3. F                mysql_read_default_file

4. h                host

5. p                password

6. P                port

7. S                mysql_socket

8. t                 table

9.u                  user

【具体执行进程解析】

现在执行一个改表语句并开启general log 观察一下

pt-online-schema-change --alter 'add column c1 int' u=username,S=/data/mysql.sock,D=test,t=a --execute

  

1. 首先就是各种show,各种set,有兴趣自己去看看,主要就是对权限的检查,超时时间的设定,当前系统的繁忙程度;然后就是对表的检查,如是否有触发器的存在,以及如下查询:

explain SELECT * FROM `test`.`a` WHERE 1=1;

SELECT table_schema, table_name FROM information_schema.key_column_usage WHERE referenced_table_schema='test' AND referenced_table_name='a';

SHOW CREATE TABLE `test`.`a`;

到这里表的情况检查完毕

2. 现在就开始建新表,注意名字的改变,a变成了_a_new

CREATE TABLE `test`.`_a_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` char(50) COLLATE utf8_bin NOT NULL,
`type` char(20) COLLATE utf8_bin NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uni_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=10000000 DEFAULT CHARSET=utf8 COLLATE=utf8_bin 

并在这个空表上直接alter

ALTER TABLE `test`.`_a_new` add column c1 int

然后做一下检查看alter是否成功

SHOW CREATE TABLE `test`.`_a_new`

   

3. 建立触发器

CREATE TRIGGER `pt_osc_test_a_del` AFTER DELETE ON `test`.`a` FOR EACH ROW DELETE IGNORE FROM `test`.`_a_new` WHERE `test`.`_a_new`.`id` <=> OLD.`id`

CREATE TRIGGER `pt_osc_test_a_upd` AFTER UPDATE ON `test`.`a` FOR EACH ROW REPLACE INTO `test`.`_a_new` (`id`, `name`, `type`, `b`) VALUES (NEW.`id`, 
NEW.`name`, NEW.`type`, NEW.`b`) CREATE TRIGGER `pt_osc_test_a_ins` AFTER INSERT ON `test`.`a` FOR EACH ROW REPLACE INTO `test`.`_a_new` (`id`, `name`, `type`, `b`) VALUES (NEW.`id`,
NEW.`name`, NEW.`type`, NEW.`b`)

  

4. 通过explain来判断执行chunk拷贝的成本,第一个chunk的大小固定为1000行,后面的chunk根据自己的指定,如chunk-time来确定大小

EXPLAIN SELECT `id`, `name`, `type`, `b` FROM `test`.`a` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1')) AND ((`id` <= '1000')) LOCK IN SHARE MODE

 确定不会影响系统正常运行后,执行insert操作,将原始表中的数据按照当前chunk大小拷贝到新表中

INSERT LOW_PRIORITY IGNORE INTO `test`.`_a_new` (`id`, `name`, `type`, `b`) SELECT `id`, `name`, `type`, `b` FROM `test`.`a` FORCE INDEX(`PRIMARY`) 
WHERE ((`id` >= '1')) AND ((`id` <= '1000')) LOCK IN SHARE MODE

 

5. 一个chunk拷贝结束后立即对系统负载进行检查

SHOW GLOBAL STATUS LIKE 'Threads_running'

 没问题的话就继续explain,insert,负载太高的话就暂停拷贝等待负载降低,以此类推,直到所有拷贝结束

6. 拷贝结束后,对新表状态是否进行检查

ANALYZE TABLE `test`.`_a_new`

 如果正常OK就往下走,如果不OK就删掉新表或不删报错退出(根据参数指定),默认是删掉

7. 确定新表没有问题后就用新表来代替旧表,注意旧表的名字在这个时候也会改一下

RENAME TABLE `test`.`a` TO `test`.`_a_old`, `test`.`_a_new` TO `test`.`a`

  

8. 替换成功后默认是将旧表删掉

DROP TABLE IF EXISTS `test`.`_a_old`

 

9. 将之前建的触发器删掉

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_a_del`

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_a_upd`

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_a_ins`

  

10. 最后再确定一下新表是否改名成功

SHOW TABLES FROM `test` LIKE '\_a\_new'

SHOW TABLES FROM `test` LIKE 'a'

  改表完成!!!

 

 

pt-online-schema-change解读的更多相关文章

  1. schema change + ogg 变更手册

    Check OGG  until no data queuing in replication process:testRO:a)login  test5 –l oggmgrb)oggc)#ggsci ...

  2. Online Schema Change for MySQL

    It is great to be able to build small utilities on top of an excellent RDBMS. Thank you MySQL. This ...

  3. AppBoxFuture(四). 随需而变-Online Schema Change

      需求变更是信息化过程中的家常便饭,而在变更过程中如何尽可能小的影响在线业务是比较头疼的事情.举个车联网监控的例子:原终端设备上传车辆的经纬度数据,新的终端设备支持同时上传速度数据,而旧的车辆状态表 ...

  4. Online, Asynchronous Schema Change in F1

    F1: A Distributed SQL Database That Scales   http://disksing.com/understanding-f1-schema-change   ma ...

  5. Online Schema Upgrade in MySQL Galera Cluster using TOI Method

    http://severalnines.com/blog/online-schema-upgrade-mysql-galera-cluster-using-toi-method     As a fo ...

  6. Schema 与数据类型优化

    这是<高性能 MySQL(第三版)>第四章<Schema 与数据类型优化>的读书笔记. 1. 选择优化的数据类型 数据类型的选择原则: 越小越好:选择满足需求的最小类型.注意, ...

  7. Mycat读写分离、主从切换、分库分表的操作记录

    系统开发中,数据库是非常重要的一个点.除了程序的本身的优化,如:SQL语句优化.代码优化,数据库的处理本身优化也是非常重要的.主从.热备.分表分库等都是系统发展迟早会遇到的技术问题问题.Mycat是一 ...

  8. Awesome Go精选的Go框架,库和软件的精选清单.A curated list of awesome Go frameworks, libraries and software

    Awesome Go      financial support to Awesome Go A curated list of awesome Go frameworks, libraries a ...

  9. mycat 笔记

    Mycat读写分离.主从切换.分库分表的操作记录   系统开发中,数据库是非常重要的一个点.除了程序的本身的优化,如:SQL语句优化.代码优化,数据库的处理本身优化也是非常重要的.主从.热备.分表分库 ...

  10. 新型MPP的Doris数据库:数据模型和数据分区使用详解

    Apache Doris是一个现代化的MPP分析性数据库产品.是一个由百度开源,在2018年贡献给Apache基金会,成为有顶级开源项目.仅需要亚秒级响应时间即可获得查询结果,可以有效地支持实时数据分 ...

随机推荐

  1. [Cycle.js] From toy DOM Driver to real DOM Driver

    This lessons shows how we are able to easily swap our toy DOM Driver with the actual Cycle.js DOM Dr ...

  2. Xcode5和6上新建工程如何本地化启动页面

    建议阅读本篇文章前先具备iOS本地化的基本知识,Google中搜索“iOS本地化”,有成片的教程~~ 最近有个app需要支持英语.简体中文.繁体中文,由于启动页面上有文字,所以也不得不做下本地化处理. ...

  3. 装Oracle12C时遇到没有权限访问临时位置的解决方法

    今天在装oracle12c是遇到了一个很奇怪的问题,显示是没有权限访问临时位置,可是我明明是用管理员的账号登陆的啊,最后在包姐的帮助下解决了,知其然,而我却不知其所以然.但还是把方法写下,希望能帮到一 ...

  4. JSP基础学习(一)

    1.jsp和servlet是javaEE规范的两个基本成员,是java web开发的重要知识,jsp和servlet本质上是一样的,因此jsp最终必须编译成servlet才能运行,或者说jsp是生成s ...

  5. struts1:(Struts)ActionForm类及表单数据验证

    在Struts的中央控制器中写了Struts的控制器角色,在这篇介绍下Struts的视图!Struts的视图组件:Struts框架中的视图组件主要包括:JSP页面.ActionForm类.Struts ...

  6. [Head First Python]5. summary

    1- "原地"排序-转换后替换 >>> list = [2,1,3] >>> list.sort() >>> list [1, ...

  7. Python之路第十三天,高级(7)-详述数据库一对多,多对多表关系的设计以及如何查询

    一对多表设计和查询方法 #!/usr/bin/env python3 # Author: Zhangxunan from sqlalchemy import create_engine from sq ...

  8. ASP.NET MVC 4.0 学习1-C#基础语法

    1,方法多載,相同的方法名稱,不同的參數類型.數量 class Program { static void Main(string[] args) { Program newObject = new ...

  9. Ubuntu14.0.4 64位安装Chrome浏览器

    下载链接:translate.google.com.hk/translate?hl=zh-CN&sl=en&u=http://95.31.35.30/chrome/pool/main/ ...

  10. C# ref_out_params方法的参数_4种类型的参数

    之前学习C#没有做笔记的习惯,因此有些基础上的东西并没有很好地整理起来,虽然这些东西比较常用,因此也没什么影响,但总觉得不整理一下感觉老是有种陌生感.今天特别整理一下C#4种类型的参数. 一.按值传递 ...