限制
- 拒绝操作没有pk、uk的表(--alter)
- 拒绝操作复制过滤(--[no]check-replication-filters)
- 拒绝外键,除非设置参数(--alter-foreign-keys-method)
- 拒绝非事务引擎表
- 暂停操作,在复制延迟时(--max-lag)
- 暂停操作,在负载高的时候(--max-load、--critical-load)
- 关注的mysql参数(innodb_lock_wait_timeout=1、lock_wait_timeout=60),可能是锁竞争受害者,但不会影响到其他事务(--set-vars)
流程
0、sbtest.t1准备做DDL操作(add column)
1、create table sbtest._t1_new xxx
2、Alter table sbtest._t1_new add xxx
3、创建三个trigger用于同步新数据
create trigger pt_osc_sbtest_t1_del
create trigger pt_osc_sbtest_t1_ins ==>replace
create trigger pt_osc_sbtest_t1_upd ==>replace
4、copy老数据
INSERT LOW_PRIORITY IGNORE INTO sbtest._t1_new xxx SELECT xxx FROM sbtest.t1 LOCK IN SHARE MODE /pt-online-schema-change 30273 copy table/
5、统计新表信息
ANALYZE TABLE sbtest._t1_new
6、交换名字
RENAME TABLE sbtest.t1 TO sbtest._t1_old, sbtest._t1_new TO sbtest.t1
7、删除旧表和触发器
OPTIONS
--host=xxx --user=xxx --password=xxx
连接实例信息,缩写-h xxx -u xxx -p xxx,密码可以使用参数--ask-pass 手动输入-
--alter
结构变更语句,不需要 ALTER TABLE关键字。与原始ddl一样可以指定多个更改,用逗号分隔。- 绝大部分情况下表上需要有主键或唯一索引,因为工具在运行当中为了保证新表也是最新的,需要旧表上创建 DELETE和UPDATE 触发器,同步到新表的时候有主键会更快。个别情况是,当alter操作就是在c1列上建立主键时,DELETE触发器将基于c1列。
- 子句不支持 rename 去给表重命名。
- alter命令原表就不支持给索引重命名,需要先drop再add,在pt-osc也一样。(mysql 5.7 支持 RENAME INDEX old_index_name TO new_index_name)
但给字段重命名,千万不要drop-add,整列数据会丢失,使用change col1 col1_new type constraint(保持类型和约束一致,否则相当于修改 column type,不能online) - 子句如果是add column并且定义了not null,那么必须指定default值,否则会失败。
- 如果要删除外键(名 fk_foo),使用工具的时候外键名要加下划线,比如--alter "DROP FOREIGN KEY _fk_foo"
D=db_name,t=table_name
指定要ddl的数据库名和表名--max-load
默认为Threads_running=25。每个chunk拷贝完后,会检查 SHOW GLOBAL STATUS 的内容,检查指标是否超过了指定的阈值。如果超过,则先暂停。这里可以用逗号分隔,指定多个条件,每个条件格式: status指标=MAX_VALUE或者status指标:MAX_VALUE。如果不指定MAX_VALUE,那么工具会这只其为当前值的120%。
因为拷贝行有可能会给部分行上锁,Threads_running 是判断当前数据库负载的绝佳指标。--max-lag
默认1s。每个chunk拷贝完成后,会查看所有复制Slave的延迟情况(Seconds_Behind_Master)。要是延迟大于该值,则暂停复制数据,直到所有从的滞后小于这个值。--check-interval配合使用,指定出现从库滞后超过 max-lag,则该工具将睡眠多长时间,默认1s,再检查。如--max-lag=5 --check-interval=2。
熟悉percona-toolkit的人都知道--recursion-method可以用来指定从库dsn记录。另外,如果从库被停止,将会永远等待,直到从开始同步,并且延迟小于该值。--chunk-time
默认0.5s,即拷贝数据行的时候,为了尽量保证0.5s内拷完一个chunk,动态调整chunk-size的大小,以适应服务器性能的变化。
也可以通过另外一个选项--chunk-size禁止动态调整,即每次固定拷贝 1k 行,如果指定则默认1000行,且比 chunk-time 优先生效--set-vars
使用pt-osc进行ddl要开一个session去操作,set-vars可以在执行alter之前设定这些变量,比如默认会设置--set-vars "wait_timeout=10000,innodb_lock_wait_timeout=1,lock_wait_timeout=60"。
因为使用pt-osc之后ddl的速度会变慢,所以预计2.5h只能还不能改完,记得加大wait_timeout。--dry-run
创建和修改新表,但不会创建触发器、复制数据、和替换原表。并不真正执行,可以看到生成的执行语句,了解其执行步骤与细节,和--print配合最佳。。--execute
确定修改表,则指定该参数。真正执行alter。–dry-run与–execute必须指定一个,二者相互排斥
为什么外键那么特殊
假设 t1 是要修改的表,t2 有外键依赖于 t1,_t1_new 是 alter t1 产生的新临时表。
这里的外键不是看t1上是否存在外键,而是作为子表的 t2。主要问题在 rename t1 时,t1“不存在”导致t2的外键认为参考失败,不允许rename。
pt-osc提供--alter-foreign-keys-method选项来决定怎么处理这种情况:
- rebuild_constraints,优先采用这种方式
- 它先通过 alter table t2 drop fk1,add _fk1 重建外键参考,指向新表
- 再 rename t1 t1_old, _t1_new t1 ,交换表名,不影响客户端
- 删除旧表 t1_old
但如果字表t2太大,以致alter操作可能耗时过长,有可能会强制选择 drop_swap。
涉及的主要方法在 pt-online-schema-change 文件的 determine_alter_fk_method, rebuild_constraints, swap_tables三个函数中。
- drop_swap,
- 禁用t2表外键约束检查 FOREIGN_KEY_CHECKS=0
- 然后 drop t1 原表
- 再 rename _t1_new t1
这种方式速度更快,也不会阻塞请求。但有风险,第一,drop表的瞬间到rename过程,原表t1是不存在的,遇到请求会报错;第二,如果因为bug或某种原因,旧表已删,新表rename失败,那就太晚了,但这种情况很少见。
我们的开发规范决定,即使表间存在外键参考关系,也不通过表定义强制约束。
注意流程
参考文章
pt-online-schema-change使用说明、限制与比较
percona-toolkit 之 【pt-online-schema-change】说明