天天看点

pt-online-schema-change使用说明、限制与比较

如果正在看这篇文章,相信你已经知道自己的需求了。

创建一个和要执行 alter 操作的表一样的新的空表结构(是alter之前的结构)

在新表执行alter table 语句(速度应该很快)

在原表中创建触发器3个触发器分别对应insert,update,delete操作

以一定块大小从原表拷贝数据到临时表,拷贝过程中通过原表上的触发器在原表进行的写操作都会更新到新建的临时表

rename 原表到old表中,在把临时表rename为原表

如果有参考该表的外键,根据alter-foreign-keys-method参数的值,检测外键相关的表,做相应设置的处理

默认最后将旧原表删除

只介绍部分常用的选项

<code>--host=xxx --user=xxx --password=xxx</code>

连接实例信息,缩写<code>-h xxx -u xxx -p xxx</code>,密码可以使用参数<code>--ask-pass</code> 手动输入。

<code>--alter</code>

结构变更语句,不需要 <code>alter table</code>关键字。与原始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,整列数据会丢失,使用<code>change col1 col1_new type constraint</code>(保持类型和约束一致,否则相当于修改 column type,不能online)

子句如果是add column并且定义了not null,那么必须指定default值,否则会失败。

如果要删除外键(名 fk_foo),使用工具的时候外键名要加下划线,比如<code>--alter "drop foreign key _fk_foo"</code>

<code>d=db_name,t=table_name</code>

指定要ddl的数据库名和表名

<code>--max-load</code>

默认为<code>threads_running=25</code>。每个chunk拷贝完后,会检查 show global status 的内容,检查指标是否超过了指定的阈值。如果超过,则先暂停。这里可以用逗号分隔,指定多个条件,每个条件格式: <code>status指标=max_value</code>或者<code>status指标:max_value</code>。如果不指定max_value,那么工具会这只其为当前值的120%。

因为拷贝行有可能会给部分行上锁,threads_running 是判断当前数据库负载的绝佳指标。

<code>--max-lag</code>

默认1s。每个chunk拷贝完成后,会查看所有复制slave的延迟情况(<code>seconds_behind_master</code>)。要是延迟大于该值,则暂停复制数据,直到所有从的滞后小于这个值。<code>--check-interval</code>配合使用,指定出现从库滞后超过 max-lag,则该工具将睡眠多长时间,默认1s,再检查。如<code>--max-lag=5 --check-interval=2</code>。

熟悉percona-toolkit的人都知道<code>--recursion-method</code>可以用来指定从库dsn记录。另外,如果从库被停止,将会永远等待,直到从开始同步,并且延迟小于该值。

<code>--chunk-time</code>

默认0.5s,即拷贝数据行的时候,为了尽量保证0.5s内拷完一个chunk,动态调整chunk-size的大小,以适应服务器性能的变化。

也可以通过另外一个选项<code>--chunk-size</code>禁止动态调整,即每次固定拷贝 1k 行,如果指定则默认1000行,且比 chunk-time 优先生效

<code>--set-vars</code>

使用pt-osc进行ddl要开一个session去操作,<code>set-vars</code>可以在执行alter之前设定这些变量,比如默认会设置<code>--set-vars "wait_timeout=10000,innodb_lock_wait_timeout=1,lock_wait_timeout=60"</code>。

因为使用pt-osc之后ddl的速度会变慢,所以预计2.5h只能还不能改完,记得加大<code>wait_timeout</code>。

<code>--dry-run</code>

创建和修改新表,但不会创建触发器、复制数据、和替换原表。并不真正执行,可以看到生成的执行语句,了解其执行步骤与细节,和<code>--print</code>配合最佳。。

<code>--execute</code>

确定修改表,则指定该参数。真正执行alter。--dry-run与--execute必须指定一个,二者相互排斥

这个很容易理解,pt-osc会在原表上创建3个触发器,而一个表上不能同时有2个相同类型的触发器,为简单通用起见,只能一棍子打死。

所以如果要让它支持有触发器存在的表也是可以实现的,思路就是:先找到原表触发器定义;重写原表触发器;最后阶段将原表触发器定义应用到新表。

这其实是我的一个顾虑,因为如果update t1,触发update t2,但这条数据还没copy到t2,不就有异常了吗?后台通过打开general_log,看到它创建的触发器:

在原表上update,新临时表上是replace into整行数据,所以达到有则更新,无则插入。同时配合后面的 insert ignore,保证这条数据不会因为重复而失败。

假设 t1 是要修改的表,t2 有外键依赖于 t1,_t1_new 是 alter t1 产生的新临时表。

这里的外键不是看t1上是否存在外键,而是作为子表的 t2。主要问题在 rename t1 时,t1“不存在”导致t2的外键认为参考失败,不允许rename。

pt-osc提供<code>--alter-foreign-keys-method</code>选项来决定怎么处理这种情况:

<code>rebuild_constraints</code>,优先采用这种方式

它先通过 alter table t2 drop fk1,add _fk1 重建外键参考,指向新表

再 rename t1 t1_old, _t1_new t1 ,交换表名,不影响客户端

删除旧表 t1_old

但如果字表t2太大,以致alter操作可能耗时过长,有可能会强制选择 drop_swap。

涉及的主要方法在 <code>pt-online-schema-change</code> 文件的 determine_alter_fk_method, rebuild_constraints, swap_tables三个函数中。

<code>drop_swap</code>,

禁用t2表外键约束检查 <code>foreign_key_checks=0</code>

然后 drop t1 原表

再 rename _t1_new t1

这种方式速度更快,也不会阻塞请求。但有风险,第一,drop表的瞬间到rename过程,原表t1是不存在的,遇到请求会报错;第二,如果因为bug或某种原因,旧表已删,新表rename失败,那就太晚了,但这种情况很少见。

我们的开发规范决定,即使表间存在外键参考关系,也不通过表定义强制约束。

使用osc会使增加一倍的空间,包括索引

而且在 row based replication 下,还会写一份binlog。不要想当然使用<code>--set-vars</code>去设置 sql_log_bin=0,因为在这个session级别,alter语句也要在从库上执行,除非你对从库另有打算。

online ddl在必须copy table时成本较高,不宜采用

pt-osc工具在存在触发器时,不适用

修改索引、外键、列名时,优先采用online ddl,并指定 algorithm=inplace

其它情况使用pt-osc,虽然存在copy data

pt-osc比online ddl要慢一倍左右,因为它是根据负载调整的

无论哪种方式都选择的业务低峰期执行

特殊情况需要利用主从特性,先alter从库,主备切换,再改原主库

借助percona博客一张图说明一下:

pt-online-schema-change使用说明、限制与比较

添加新列

完整输出过程

修改列类型

添加删除索引

放后台执行

修改主键

1. 存在trigger

表上存在触发器,不适用。

2. no-version-check

<a href="https://www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html">refman pt-online-schema-change</a>

<a href="https://help.aliyun.com/knowledge_detail/13098164.html">rds mysql 如何使用 percona toolkit</a>

<a href="http://www.cnblogs.com/zhoujinyi/p/3491059.html">percona-toolkit 之 【pt-online-schema-change】说明</a>

<a href="https://www.percona.com/blog/2014/11/18/avoiding-mysql-alter-table-downtime/">avoiding mysql alter table downtime</a>

<a href="http://www.imcjd.com/?p=1081">mysql online ddl和nosql schemaless design</a>