gh-ost
gh-ost1.0.45要求go 1.9+版本
---------------------------------------------------------------------------------------------
1.go源码安装
# 安装go依赖包
sudo apt-get install bison ed gawk gcc libc6-dev make
# 配置go环境变量,GOROOT为go源码目录,GOPATH为gh-ost这个工程的目录
echo "
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/usr/local/go/src/github.com/github/gh-ost">>~/.bashrc && source ~/.bashrc
# 获取+解压go源码安装包,go下载地址:https://golang.org/dl/
tar -zxvf go1.9.6.linux-amd64.tar.gz -C /usr/local/
# 此时是go的安装目录为/usr/local/go
# 验证go安装成功
go env
2.gh-ost二进制安装
tar xzvf /soft/gh-ost-binary-linux-20180417090238.tar.gz -C /usr/local/
ln -s /usr/local/gh-ost /usr/bin/gh-ost
# 验证gh-ost安装成功
gh-ost -version
---------------------------------------------------------------------------------------------
主:3306 从:3307(支持多从库,测试一下)
---------------------------------------------------------------------------------------------
用户权限:
表需要的权限: ALTER, CREATE, DELETE, DROP, INDEX, INSERT, LOCK TABLES, SELECT, TRIGGER, UPDATE 简单方式(*.*)
复制需要的权限:
statement格式:SUPER, REPLICATION SLAVE on *.*
(SUPER是为了在SBR方式stop slave,start slave;主库搭配assume-rbr=true参数,可以做到不需要启停slave操作,也就不需要SUPER权限;如果要在从库进行测试就需要super权限)
row格式:REPLICATION CLIENT, REPLICATION SLAVE on *.*
以row格式为例:
create user u01@'%' identified by 'u01';
GRANT ALL PRIVILEGES ON db3306.* TO u01@'%' ;
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO u01@'%';
---------------------------------------------------------------------------------------------
限制:
外键不支持
触发器不支持
5.7 generated列(函数运算)不支持
POINT列不支持
JSON列支持,但是不能是主键部分
要有主键或者唯一索引(或者有允许null的键,但是实际上键值并没有null的值)
更改不能同名(大小写不一样的也不行,比如MyTable 和 MYtable)
多源复制只能在主库进行操作
主主模式只支持单节点对表修改(多节点都在修改表的方式不支持)
ALTER TABLE ... RENAME TO some_other_name 不支持
---------------------------------------------------------------------------------------------
工作模式:
1.连上从库,在主库上修改
2.直接连上主库
3.从库修改和测试
1.连上从库,在主库上修改
gh-ost \
--host=127.0.0.1 \
--port=3307 \
--user=u01 \
--password=u01 \
--database=db3306 \
--table=t01 \
--alter="ADD COLUMN x INT NOT NULL DEFAULT '0'" \
--chunk-size=2000 \
--max-load=Threads_running=20 \
--critical-load=Threads_running=1000 \
--assume-rbr=true \
--postpone-cut-over-flag-file=/tmp/gh-postpone-flag \
--panic-flag-file=/tmp/gh-panic-flag \
--initially-drop-old-table \
--initially-drop-ghost-table \
--execute
2.直接连上主库(可以不用设置assume-rbr参数,直接主库操作,原理不会对从库进行启停操作)
gh-ost \
--host=127.0.0.1 \
--port=3306 \
--user=u01 \
--password=u01 \
--database=db3306 \
--table=t01 \
--alter="drop COLUMN x" \
--chunk-size=2000 \
--max-load=Threads_running=20 \
--critical-load=Threads_running=1000 \
--assume-rbr=true \
--postpone-cut-over-flag-file=/tmp/gh-postpone-flag \
--panic-flag-file=/tmp/gh-panic-flag \
--initially-drop-old-table \
--initially-drop-ghost-table \
--allow-on-master \
--execute
3.从库测试(test-on-replica)--需要启停slave的权限(super权限)
gh-ost \
--host=127.0.0.1 \
--port=3307 \
--user=u01 \
--password=u01 \
--database=db3306 \
--table=t01 \
--alter="ADD COLUMN x INT NOT NULL DEFAULT '0'" \
--chunk-size=2000 \
--max-load=Threads_running=20 \
--critical-load=Threads_running=1000 \
--assume-rbr=true \
--postpone-cut-over-flag-file=/tmp/gh-postpone-flag \
--panic-flag-file=/tmp/gh-panic-flag \
--initially-drop-old-table \
--initially-drop-ghost-table \
--test-on-replica \
--verbose \
--execute
昨晚之后会暂停slave下来,做对比
mysql -e -S /tmp/glp3307.sock -uroot -pgouliping 'select * from db3306.t01 order by uid' | md5sum
mysql -e -S /tmp/glp3307.sock -uroot -pgouliping 'select * from db3306._t01_gho order by uid' | md5sum
做完对比后,删除掉ghost表,然后start slave即可恢复
从库直接修改(会破坏主从复制):
migrate-on-replica:直接在从库上进行迁移操作。即使在复制运行阶段也可以进行表的切换操作。
手工暂停:
echo throttle | nc -U /tmp/gh-ost.test.sample_data_0.sock
echo no-throttle | nc -U /tmp/gh-ost.test.sample_data_0.sock
#我们使用socat替代nc
暂停:echo throttle | socat - /tmp/gh-ost.db3306.t01.sock
重启:echo no-throttle | socat - /tmp/gh-ost.db3306.t01.sock
重要参数:
allow-on-masterr: 默认连接从库,加上这个参数可以直接连接主库
conf:配置文件
critical-load:超过则退出
max-load:超过则暂停
cut-over:默认atomic(原子性的rename请求,在所有被blocked的请求中,优先级永远是最高的),还有two-step(采用facebooks osc方式,原表先重命名,_gho表再命名到原表),酌情选择
discard-foreign-keys:不要外键
migrate-on-replica:直接在从库上进行迁移操作。即使在复制运行阶段也可以进行表的切换操作。
test-on-replica:只为了测试,在切换之前复制会停止,然后会进行切换操作,然后在切换回来,你的原始表最终还是原始表。两个表都会保存下来;cut-over后复制操作是停止的,可进行一致性检查等测试操作
postpone-cut-over-flag-file:延迟切换;设置的标志文件如果不存在,gh-ost会自动切换,如果存在则继续监控数据更新(生产这个有用,操作可控)
panic-flag-file:中断标志文件如果存在,马上中断操作,并且不清理数据
conf:配置文件
critical-load:超过则退出
max-load:超过则暂停
cut-over:默认atomic(原子性的rename请求,在所有被blocked的请求中,优先级永远是最高的),还有two-step(采用facebooks osc方式,原表先重命名,_gho表再命名到原表),酌情选择
discard-foreign-keys:不要外键
migrate-on-replica:直接在从库上进行迁移操作。即使在复制运行阶段也可以进行表的切换操作。
test-on-replica:只为了测试,在切换之前复制会停止,然后会进行切换操作,然后在切换回来,你的原始表最终还是原始表。两个表都会保存下来;cut-over后复制操作是停止的,可进行一致性检查等测试操作
postpone-cut-over-flag-file:延迟切换;设置的标志文件如果不存在,gh-ost会自动切换,如果存在则继续监控数据更新(生产这个有用,操作可控)
panic-flag-file:中断标志文件如果存在,马上中断操作,并且不清理数据
---------------------------------------------------------------------------------------------
#各项对比
---------------------------------------------------------------------------------------------
失败处理:
可能发生风险的点:cut-over 阶段,会短暂的锁表
其他情况:因为原理是读取binlog到写入另外的表,如果在复制过程中失败,完全不用理会,可以删除_gh开头的两个表,重新进行操作即可
#其他参考### cut-over如果失败
https://yq.aliyun.com/articles/62928
gh-ost利用了MySQL的一个特性,就是原子性的rename请求,在所有被blocked的请求中,优先级永远是最高的。gh-ost基于此设计了该方案:一个连接对原表加锁,另启一个连接尝试rename操作,此时会被阻塞住,当释放lock的时候,rename会首先被执行,其他被阻塞的请求会继续应用到新表。
migrator.go:iterateChunks() 函数来确定何时开始cut-over
具体切换流程如下:
https://github.com/github/gh-ost/issues/82
START(会话A和会话B)
会话A:CREATE table tbl_old(防止rename过早执行)
会话A:LOCK TABLES tbl WRITE, tbl_old WRITE
应用会话:的请求进来,关于原表的请求被blocked
会话B:RENAME TABLE tbl TO tbl_old, ghost TO tbl , 同样被blocked
应用会话:新的请求进来,关于原表的请求被blocked
会话A:检查是否有blocked 的RENAME请求是否完成,通过show processlist
会话A:DROP TABLE tbl_old --会删掉tbl_old表
会话A:UNLOCK TABLES
RENAME SUCCESS
END
不同阶段失败后如何处理:
如果第一步失败,退出程序
如果会话A建表成功,加锁失败,退出程序,未加锁 --手工测试,如果应用会话start transaction,则会话A根本无法加锁成功
rename请求来的时候,会话A死掉,lock会自动释放,同时因为tbl_old的存在rename也会失败,所有请求恢复正常
rename被blocked的时候,会话A死掉,lock会自动释放,同样因为tbl_old的存在,rename会失败,所有请求恢复正常
rename死掉,gh-ost会捕获不到rename,会话A继续运行,释放lock,所有请求恢复正常
No matter what happens, at the end of operation we look for the ghost table. Is it still there? Then we know the operation failed, "atomically". Is it not there? Then it has been renamed to tbl, and the operation worked atomically.
无论怎样,其实结果就是看ghost表是否还在,如果还在那就失败了
------------------------------------------其他验证------------------------------------------
功能无问题,达到max-load会暂停,critical-load会退出进程
max-load:20
critical-load:30
mysql> show status like 'threads_running';
sysbench /usr/share/sysbench/oltp_read_only.lua --mysql-host=10.6.1.211 --mysql-port=3306 --mysql-user=root --mysql-password=gouliping --mysql-db=sbtest --db-driver=mysql --tables=20 --table-size=1000000 prepare
sysbench /usr/share/sysbench/oltp_read_only.lua --mysql-host=10.6.1.211 --mysql-port=3306 --mysql-user=root --mysql-password=gouliping --mysql-db=sbtest --db-driver=mysql --tables=20 --table-size=1000000 --threads=12 --report-interval=10 --rand-type=uniform --time=600 run
Copy: 3732000/25141732 14.8%; Applied: 0; Backlog: 0/1000; Time: 2m10s(total), 2m10s(copy); streamer: mysql-bin.000039:691391902; State: throttled, max-load Threads_running=25 >= 20; ETA
sysbench /usr/share/sysbench/oltp_read_only.lua --mysql-host=10.6.1.211 --mysql-port=3306 --mysql-user=root --mysql-password=gouliping --mysql-db=sbtest --db-driver=mysql --tables=20 --table-size=1000000 --threads=30 --report-interval=10 --rand-type=uniform --time=600 run
2018-05-28 14:27:21 FATAL critical-load met: Threads_running=33, >=20
cut-over 手工加事务,看是失败还是成功(pt-osc如果有大事务当时在运行就直接没法操作)
测试方法:
开个其他会话,在开始之前进行大事务操作-->start transaction; update t01 set add_time=now();
有事务的情况下,根本启动不了,达到innodb_lock_wait_timeout设置的超时过后,就失败退出,报错FATAL Error 1205: Unknown error 1205
MySQL error code 1205 (ER_LOCK_WAIT_TIMEOUT): Lock wait timeout exceeded; try restarting transaction--到了innodb_lock_wait_timeout设置的50s超时过后,就失败退出
https://github.com/github/gh-ost/blob/master/doc/what-if.md
What if the cut-over (table switch) is unable to proceed due to locks/timeout?
There is a lock_wait_timeout explicitly associated with the cut-over operation. If your table suddenly suffers from a long running query, the cut-over (involving LOCK and RENAME statements) may be unable to proceed. There's a finite number of retries, and if none of these succeeds, gh-ost bails out.
开个其他会话,在gh-ost运行中、快到cut-over的时候进行大事务-->start transaction; update t01 set add_time=now();
反复获取lock tables ( 会话A:Waiting for table metadata lock :LOCK TABLES tbl WRITE, tbl_old WRITE) 这一步,参考上面原理
FATAL Error 1205: Unknown error 1205
如果大事务提交, ERROR Timeout while waiting for events up to lock --->ghost表也要重演大事务,耗时很多,后台实际上会等待一段时间更新ghost表,原理上不会对原表有什么影响
拿不到lock tabkle write的锁,会反复重试
====>
如果事务提交,等待一段时间,会正常切换;
如果事务一直没提交,真正超时参数:数据库的innodb_lock_wait_timeout参数 + gh-ost的default-retries参数(默认60次)====> 超时总时间
测试方法:
开个其他会话,在开始之前进行大事务操作-->start transaction; update t01 set add_time=now();
有事务的情况下,根本启动不了,达到innodb_lock_wait_timeout设置的超时过后,就失败退出,报错FATAL Error 1205: Unknown error 1205
MySQL error code 1205 (ER_LOCK_WAIT_TIMEOUT): Lock wait timeout exceeded; try restarting transaction--到了innodb_lock_wait_timeout设置的50s超时过后,就失败退出
https://github.com/github/gh-ost/blob/master/doc/what-if.md
What if the cut-over (table switch) is unable to proceed due to locks/timeout?
There is a lock_wait_timeout explicitly associated with the cut-over operation. If your table suddenly suffers from a long running query, the cut-over (involving LOCK and RENAME statements) may be unable to proceed. There's a finite number of retries, and if none of these succeeds, gh-ost bails out.
开个其他会话,在gh-ost运行中、快到cut-over的时候进行大事务-->start transaction; update t01 set add_time=now();
反复获取lock tables ( 会话A:Waiting for table metadata lock :LOCK TABLES tbl WRITE, tbl_old WRITE) 这一步,参考上面原理
FATAL Error 1205: Unknown error 1205
如果大事务提交, ERROR Timeout while waiting for events up to lock --->ghost表也要重演大事务,耗时很多,后台实际上会等待一段时间更新ghost表,原理上不会对原表有什么影响
拿不到lock tabkle write的锁,会反复重试
====>
如果事务提交,等待一段时间,会正常切换;
如果事务一直没提交,真正超时参数:数据库的innodb_lock_wait_timeout参数 + gh-ost的default-retries参数(默认60次)====> 超时总时间
达到真正超时时间后,直接退出进程,销毁socketx
-cut-over-lock-timeout-seconds
配合上面设置超时时间10秒(最大只能设置到10秒),默认3秒
retry attempted when lock exceeds timeout 作用是超时之后重试的超时时间
-cut-over-lock-timeout-seconds
配合上面设置超时时间10秒(最大只能设置到10秒),默认3秒
retry attempted when lock exceeds timeout 作用是超时之后重试的超时时间
----------------------------------------------------------------------------------------------------
gh-ost如果切换中(主从断开了,是否还会继续?)
如果拷贝过程中,手工stop slave会发现,gh-ost会进入到暂停状态
如果cut-over过程暂停,一样的会进入到暂停状态
作者:lcrash
出处:http://www.cnblogs.com/lcrash/
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。