当前位置: 首页 > 工具软件 > gh-ost > 使用案例 >

gh-ost

许安邦
2023-12-01

GitHubMySQL无触发器在线更改表定义工具!

目的是解决一个经常碰到的问题:不断变化的产品需求会不断要求更改MySQL表结构。gh-ost通过一种影响小、可控制、可审计、操作简单的方案来改变线上表结构。

目前,在线修改表定义的任务主要是通过这三种途径完成的:

1.在从库上修改表定义,修改之后再提升为新的主库。

2.通过MySQLInnoDB在线DDL功能。

3.使用修改表定义工具。现在最流行的是Percona公司的pt-online-schema-changeFacebookOSC

1)在从库上修改表定义的方案需要付出许多运维代价,这需要更多的服务器、更长的完成时间和更复杂的管理工作。修改操作是直接应用在具体的某个从库或者整个拓扑架构的一些子树上。服务器宕机、从库数据不够新、新部署的服务器等各种问题都需要有非常严密的跟踪系统来跟进单个数据库上的操作。一个改变操作可能会需要多次反复,也就需要更长时间。而把一个从库升为主库也会导致短暂的停服。如果同时需要做多个更改就更难协调。由于每天都要改好几张表,所以在考虑解决方案时不希望有这样的管理开销。

2)MySQLInnoDB在线DDL只能是在你敲命令的那个MySQL上才是“在线”修改的。二进制文件中的日志把修改操作序列化了,从库应用日志时会导致复制延迟。但如果尝试在每个从库上挨个去改的话又会导致上面分析的管理代价。而且DDL还是不可中断的,要是在修改时把操作杀掉的话还需要更长的时间去回滚,甚至导致数据字典崩溃。这种方案也不“友好”,在系统负载高时也不能限速或者暂停。这样的操作还有可能会耗尽你的系统资源。

3)Github用了pt-online-schema-change好几年了。可是,当生产系统数据增多、业务压力增大之后就碰到了越来越多的问题,甚至到了许多修改操作都被认为是“危险操作”的地步。有一些操作只敢在非业务高峰期或者周末才敢执行,其它的总是会导致MySQL停止服务。所有现有的在线修改表定义工具都是用MySQL触发器来迁移数据的,因此本身就存在着一些问题。

 

gh-ost有以下特点:

无触发器:

gh-ost不使用触发器,它跟踪二进制日志文件,在对原始表的修改提交之后,用异步方式把这修改内容应用到临时表中去。

轻量级:

因为不需要使用触发器,gh-ost把修改表定义的负载和正常的业务负载解耦开了。它不需要考虑被修改的表上的并发操作和竞争等,这些在二进制日志中都被序列化了,gh-ost只操作临时表,完全与原始表不相干。事实上,gh-ost也把行拷贝的写操作与二进制日志的写操作序列化了,这样,对主库来说只是有一条连接在顺序的向临时表中不断写入数据,这样的行为与常见的ETL相当不同。

可暂停:

因为所有写操作都是gh-ost生成的,而读取二进制文件本身就是一个异步操作,所以在暂停时,gh-ost是完全可以把所有对主库的写操作全都暂停的。暂停就意味着对主库没有写入和更新。不过gh-ost也有一张内部状态跟踪表,即使在暂停状态下也会向那张表中不断写入心跳信息,写入量可以忽略不计。

动态可控:

gh-ost通过监听TCP或者unix socket文件来获取命令。即使有正在进行中的修改工作,用户也可以向gh-ost发出命令修改配置,比如可以这样做:

echo throttle | socat - /tmp/gh-ost.sock:这是暂停命令。也可以输入no-throttle

修改运行参数,gh-ost可以接受这样的修改方式来改变它的行为:chunk-size=1500, max-lag-millis=2000, max-load=Thread_running=30

可审计:

用上面所说的相同接口也可以查看gh-ost的状态,查看当前任务进度、主要配置参数、相关MySQL实例的情况等。这些信息通过网络发送命令就可以得到,因此就给了运维人员极大的灵活性,如果是使用别的工具的话一般只能是通过共享屏幕或者不断跟踪日志文件最新内容。

可测试:

读取二进制文件内容的操作完全不会增加主库的负载,在从库上做修改表结构的操作也和在主库上做是非常相象的(当然并不完全一样,但主要来说还是差不多的)。gh-ost自带了--test-on-replica选项来支持测试功能,它允许你在从库上运行起修改表结构操作,在操作结束时会暂停主从复制,让两张表都处于同步、就绪状态,然后切换表、再切换回来。这样就可以让用户从容不迫地对两张表进行检查和对比。

可靠性高:

1)Github在从库上对gh-ost进行测试,在去主库上做第一次真正改动之前在从库上成功地试了几千次。所以,请你也在从库上开始测试,验证数据是完好无损的,然后再把它用到生产环境。希望你可以放手去试。

2)当你执行了gh-ost之后,也许你会看见主库的负载变高了,那你可以发出暂停命令。用echo throttle命令生成一个文件,看看主库的负载会不会又变得正常。试一下这些命令,你就可以知道你可以怎样控制它的行为,你的心里就会安定许多。

3)你发起了一次修改操作,然后估计完成时间是凌晨2点钟,可是你又非常关心最后的切换操作,非常想看着它切换,这可怎么办?只需要一个标志位文件就可以告诉gh-ost推迟切换了,这样gh-ost会只做完拷贝数据的操作,但不会切换表。它还会仍然继续同步数据,保持临时表的数据处于同步状态。等第二天早上你回到办公室之后,删除标志位文件或者向gh-ost发送命令echo unpostpone,它就会做切换了。没有人希望软件强迫自己看着它做事情,它应该把大家解放出来,让人去做人该做的事。

4)谈到估计完成时间,--exact-rowcount选项非常有用。在最开始时要在目标表上做个代价比较大的SELECT COUNT(*)操作查出具体要拷多少行数据,gh-ost就会对它要做多少工作有了一个比较准确的估计。接下来在拷贝的过程中,它会不断地尝试更新这个估计值。因为预计完成的时间点总是会不断变化,所以已经完成的百分比就反而比较精确。如果你也曾经有过非常痛苦的经历,看着已经完成99%了可是剩下的一点操作却继续了一个小时也没完,你就会非常喜欢gh-ost提供的这个功能。

 

1.安装Go语言环境(Go版本1.9及以上)

源码安装包

tar -zxvf go1.9.6.linux-amd64.tar.gz -C /usr/local/

# 此时是go的安装目录为/usr/local/go

添加环境变量

export PATH=$PATH:/usr/local/go/bin

export GOROOT=/usr/local/go

export GOPATH=/app/gowork

# 验证go安装成功

go env

 

# 获取+解压gh-ost源码安装包,

gh-ost下载地址:

https://github.com/github/gh-ost/releases/download/v1.0.46/gh-ost-binary-linux-20180527215024.tar.gz

 

# gh-ost源码安装

tar -zxvf gh-ost-binary-linux-20180527215024.tar.gz -C /usr/local

ln -s /usr/local/gh-ost /usr/bin/gh-ost

 

# 验证gh-ost安装成功

gh-ost -version

1.0.46

gh-ost --help

# 结果会输出一堆参数,gh-ost参数待以后详解

 

 

gh-ost工作模式

gh-ost工作时可以连上多个MySQL实例,同时也把自己以从库的方式连上其中一个实例来获取二进制日志事件。根据你的配置、数据库集群架构和你想在哪里执行修改操作,可以有许多种不同的工作模式。

1、连上从库,在主库上修改

这是gh-ost默认的工作模式,它会查看从库情况,找到集群的主库并且连接上去。修改操作的具体步骤是:

在主库上读写行数据;

在从库上读取二进制日志事件,将变更应用到主库上;

在从库上查看表格式、字段、主键、总行数等;

在从库上读取gh-ost内部事件日志(比如心跳);

在主库上完成表切换;

连接从库进行操作:

gh-ost --user="dba_user" --password="msds007" --host='192.168.1.102' --port=3306 --database="mytest" --table="tt" --verbose --debug --stack --alter="add COLUMN retention_flag10 int(11)" --initially-drop-ghost-table --initially-drop-old-table --ok-to-drop-table --max-load=Threads_running=30 --chunk-size=2000  --execute

 

2、连上主库

如果没有从库,或者不想在从库上操作,那直接用主库也是可以的。gh-ost就会在主库上直接做所有的操作。仍然可以在上面查看主从复制延迟。

主库必须产生Row格式的二进制日志;

启动gh-ost时必须用--allow-on-master选项来开启这种模式;

连接主库进行操作:

gh-ost --user="dba_user" --password="msds007" --host='192.168.1.101' --port=3306 --database="mytest" --table="tt" --verbose --allow-on-master --debug --stack --alter="add COLUMN retention_flag10 int(11)" --initially-drop-ghost-table --initially-drop-old-table  --max-load=Threads_running=30 --chunk-size=2000

 

gh-ost --user="dba_user" --password="msds007" --host='192.168.1.101' --port=3306 --database="mytest" --table="tt" --verbose --allow-on-master --debug --stack --alter="add COLUMN retention_flag10 int(11)" --initially-drop-ghost-table --initially-drop-old-table  --max-load=Threads_running=30 --chunk-size=2000  --execute

 

gh-ost --user="dba_user" --password="msds007" --host='192.168.1.101' --port=3306 --database="mytest" --table="tt" --verbose --allow-on-master --debug --stack --alter="add COLUMN retention_flag10 int(11)" --initially-drop-ghost-table --initially-drop-old-table --ok-to-drop-table --max-load=Threads_running=30 --chunk-size=2000  --execute

 

--initially-drop-ghost-table:gh-ost操作之前,检查并删除已经存在的ghost表。该参数不建议使用,请手动处理原来存在的ghost表。默认不启用该参数,gh-ost直接退出操作。

--initially-drop-old-table:gh-ost操作之前,检查并删除已经存在的旧表。该参数不建议使用,请手动处理原来存在的ghost表。默认不启用该参数,gh-ost直接退出操作。

--initially-drop-socket-file:gh-ost强制删除已经存在的socket文件。该参数不建议使用,可能会删除一个正在运行的gh-ost程序,导致DDL失败。

--ok-to-drop-table:gh-ost操作结束后,删除旧表,默认状态是不删除旧表,会存在_tablename_del表。

 

3、在从库上修改和测试

这种模式会在从库上做修改。gh-ost仍然会连上主库,但所有操作都是在从库上做的,不会对主库产生任何影响。在操作过程中,gh-ost也会不时地暂停,以便从库的数据可以保持最新。

--migrate-on-replica选项让gh-ost直接在从库上修改表。最终的切换过程也是在从库正常复制的状态下完成的。

--test-on-replica表明操作只是为了测试目的。在进行最终的切换操作之前,复制会被停止。原始表和临时表会相互切换,再切换回来,最终相当于原始表没被动过。主从复制暂停的状态下,你可以检查和对比这两张表中的数据。

 

 

常用参数说明:

--max-load

迁移过程中,gh-ost会时刻关注负载情况,负载阀值是使用者自己定义,比如数据库的最大连接数,如果超过阀值,gh-ost不会退出,会等待到负载在阀值以下继续执行。

--critical-load

这个指的是gh-ost退出阀值,当负载超过这个阀值,gh-ost会停止并退出

--chunk-size

迁移过程是一步步分批次完成的,这个参数是指事务每次提交的行数,默认是1000

--max-lag-millis

会监控从库的主从延迟情况,如果延迟秒数超过这个阀值,迁移不会退出,等待延迟秒数低于这个阀值继续迁移。

--throttle-control-replicas

--max-lag-millis参数相结合,这个参数指定主从延迟的数据库实例。

--initially-drop-ghost-table

gh-ost执行前会创建两张xx_ghcxx_gho表,如果这两张表存在,且加上了这个参数,那么会自动删除原gh表,从新创建,否则退出。xx_gho表相当于老表的全量备份,xx_ghc表数据是数据更改日志,理解成增量备份。

--initially-drop-socket-file

gh-ost执行时会创建socket文件,退出时不会删除,下次执行gh-ost时会报错,加上这个参数会删除老的socket文件,重新创建。

--ok-to-drop-table

go-ost执行完以后是否删除老表,加上此参数会自动删除老表。

--host

数据库实例地址。

--port

数据库实例端口。

--user

数据库实例用户名。

--password

数据库实例密码。

--database

数据库名称。

--table

表名。

-verbose

执行过程输出日志。

--alter

操作语句。

--cut-over

自动执行rename操作。

--debug

输出详细日志。

--panic-flag-file

这个文件被创建,迁移操作会被立即终止退出。

--execute

如果确定执行,加上这个参数。

--allow-on-master

整个迁移所有操作在主库上执行,也就是数的b方法。

--throttle-flag-file

此文件存在时操作暂停,删除文件操作会继续。

 

# gh-ost --help

Usage of gh-ost:

  -allow-master-master

     explicitly allow running in a master-master setup

  -allow-nullable-unique-key

     allow gh-ost to migrate based on a unique key with nullable columns. As long as no NULL values exist, this should be OK. If NULL values exist in chosen key, data may be corrupted. Use at your own risk!

  -allow-on-master

     allow this migration to run directly on master. Preferably it would run on a replica

  -alter string

     alter statement (mandatory)

  -approve-renamed-columns ALTER

     in case your ALTER statement renames columns, gh-ost will note that and offer its interpretation of the rename. By default gh-ost does not proceed to execute. This flag approves that gh-ost's interpretation is correct

  -ask-pass

     prompt for MySQL password

  -assume-master-host string

     (optional) explicitly tell gh-ost the identity of the master. Format: some.host.com[:port] This is useful in master-master setups where you wish to pick an explicit master, or in a tungsten-replicator where gh-ost is unable to determine the master

  -assume-rbr

     set to 'true' when you know for certain your server uses 'ROW' binlog_format. gh-ost is unable to tell, event after reading binlog_format, whether the replication process does indeed use 'ROW', and restarts replication to be certain RBR setting is applied. Such operation requires SUPER privileges which you might not have. Setting this flag avoids restarting replication and you can proceed to use gh-ost without SUPER privileges

  -check-flag

     Check if another flag exists/supported. This allows for cross-version scripting. Exits with 0 when all additional provided flags exist, nonzero otherwise. You must provide (dummy) values for flags that require a value. Example: gh-ost --check-flag --cut-over-lock-timeout-seconds --nice-ratio 0

  -chunk-size int

     amount of rows to handle in each iteration (allowed range: 100-100,000) (default 1000)

  -concurrent-rowcount

     (with --exact-rowcount), when true (default): count rows after row-copy begins, concurrently, and adjust row estimate later on; when false: first count rows, then start row copy (default true)

  -conf string

     Config file

  -critical-load string

     Comma delimited status-name=threshold, same format as --max-load. When status exceeds threshold, app panics and quits

  -critical-load-hibernate-seconds int

     When nonzero, critical-load does not panic and bail out; instead, gh-ost goes into hibernate for the specified duration. It will not read/write anything to from/to any server

  -critical-load-interval-millis int

     When 0, migration immediately bails out upon meeting critical-load. When non-zero, a second check is done after given interval, and migration only bails out if 2nd check still meets critical load

  -cut-over string

     choose cut-over type (default|atomic, two-step) (default "atomic")

  -cut-over-lock-timeout-seconds int

     Max number of seconds to hold locks on tables while attempting to cut-over (retry attempted when lock exceeds timeout) (default 3)

  -database string

     database name (mandatory)

  -debug

     debug mode (very verbose)

  -default-retries int

     Default number of retries for various operations before panicking (default 60)

  -discard-foreign-keys

     DANGER! This flag will migrate a table that has foreign keys and will NOT create foreign keys on the ghost table, thus your altered table will have NO foreign keys. This is useful for intentional dropping of foreign keys

  -dml-batch-size int

     batch size for DML events to apply in a single transaction (range 1-100) (default 10)

  -exact-rowcount

     actually count table rows as opposed to estimate them (results in more accurate progress estimation)

  -execute

     actually execute the alter & migrate the table. Default is noop: do some tests and exit

  -force-named-cut-over

     When true, the 'unpostpone|cut-over' interactive command must name the migrated table

  -force-table-names string

     table name prefix to be used on the temporary tables

  -heartbeat-interval-millis int

     how frequently would gh-ost inject a heartbeat value (default 100)

  -help

     Display usage

  -hooks-hint string

     arbitrary message to be injected to hooks via GH_OST_HOOKS_HINT, for your convenience

  -hooks-path string

     directory where hook files are found (default: empty, ie. hooks disabled). Hook files found on this path, and conforming to hook naming conventions will be executed

  -host string

     MySQL hostname (preferably a replica, not the master) (default "127.0.0.1")

  -initially-drop-ghost-table

     Drop a possibly existing Ghost table (remains from a previous run?) before beginning operation. Default is to panic and abort if such table exists

  -initially-drop-old-table

     Drop a possibly existing OLD table (remains from a previous run?) before beginning operation. Default is to panic and abort if such table exists

  -initially-drop-socket-file

     Should gh-ost forcibly delete an existing socket file. Be careful: this might drop the socket file of a running migration!

  -master-password string

     MySQL password on master, if different from that on replica. Requires --assume-master-host

  -master-user string

     MySQL user on master, if different from that on replica. Requires --assume-master-host

  -max-lag-millis int

     replication lag at which to throttle operation (default 1500)

  -max-load string

     Comma delimited status-name=threshold. e.g: 'Threads_running=100,Threads_connected=500'. When status exceeds threshold, app throttles writes

  -migrate-on-replica

     Have the migration run on a replica, not on the master. This will do the full migration on the replica including cut-over (as opposed to --test-on-replica)

  -nice-ratio float

     force being 'nice', imply sleep time per chunk time; range: [0.0..100.0]. Example values: 0 is aggressive. 1: for every 1ms spent copying rows, sleep additional 1ms (effectively doubling runtime); 0.7: for every 10ms spend in a rowcopy chunk, spend 7ms sleeping immediately after

  -ok-to-drop-table

     Shall the tool drop the old table at end of operation. DROPping tables can be a long locking operation, which is why I'm not doing it by default. I'm an online tool, yes?

  -panic-flag-file string

     when this file is created, gh-ost will immediately terminate, without cleanup

  -password string

     MySQL password

  -port int

     MySQL port (preferably a replica, not the master) (default 3306)

  -postpone-cut-over-flag-file string

     while this file exists, migration will postpone the final stage of swapping tables, and will keep on syncing the ghost table. Cut-over/swapping would be ready to perform the moment the file is deleted.

  -quiet

     quiet

  -replica-server-id uint

     server id used by gh-ost process. Default: 99999 (default 99999)

  -replication-lag-query string

     Deprecated. gh-ost uses an internal, subsecond resolution query

  -serve-socket-file string

     Unix socket file to serve on. Default: auto-determined and advertised upon startup

  -serve-tcp-port int

     TCP port to serve on. Default: disabled

  -skip-foreign-key-checks

     set to 'true' when you know for certain there are no foreign keys on your table, and wish to skip the time it takes for gh-ost to verify that

  -skip-renamed-columns ALTER

     in case your ALTER statement renames columns, gh-ost will note that and offer its interpretation of the rename. By default gh-ost does not proceed to execute. This flag tells gh-ost to skip the renamed columns, i.e. to treat what gh-ost thinks are renamed columns as unrelated columns. NOTE: you may lose column data

  -stack

     add stack trace upon error

  -switch-to-rbr

     let this tool automatically switch binary log format to 'ROW' on the replica, if needed. The format will NOT be switched back. I'm too scared to do that, and wish to protect you if you happen to execute another migration while this one is running

  -table string

     table name (mandatory)

  -test-on-replica

     Have the migration run on a replica, not on the master. At the end of migration replication is stopped, and tables are swapped and immediately swap-revert. Replication remains stopped and you can compare the two tables for building trust

  -test-on-replica-skip-replica-stop

     When --test-on-replica is enabled, do not issue commands stop replication (requires --test-on-replica)

  -throttle-additional-flag-file string

     operation pauses when this file exists; hint: keep default, use for throttling multiple gh-ost operations (default "/tmp/gh-ost.throttle")

  -throttle-control-replicas string

     List of replicas on which to check for lag; comma delimited. Example: myhost1.com:3306,myhost2.com,myhost3.com:3307

  -throttle-flag-file string

     operation pauses when this file exists; hint: use a file that is specific to the table being altered

  -throttle-http string

     when given, gh-ost checks given URL via HEAD request; any response code other than 200 (OK) causes throttling; make sure it has low latency response

  -throttle-query string

     when given, issued (every second) to check if operation should throttle. Expecting to return zero for no-throttle, >0 for throttle. Query is issued on the migrated server. Make sure this query is lightweight

  -timestamp-old-table

     Use a timestamp in old table name. This makes old table names unique and non conflicting cross migrations

  -tungsten

     explicitly let gh-ost know that you are running on a tungsten-replication based topology (you are likely to also provide --assume-master-host)

  -user string

     MySQL user

  -verbose

     verbose

  -version

     Print version & exit

 

转载于:https://www.cnblogs.com/allenhu320/p/11339046.html

 类似资料: