技術分享 | gh-ost 在線 ddl 變動工具

做者簡介:
楊奇龍,網名「北在南方」,7年DBA老兵,目前任職於杭州有贊科技DBA,主要負責數據庫架構設計和運維平臺開發工做,擅長數據庫性能調優、故障診斷。

1、前言

做爲 MySQL DBA,相信咱們你們都會對大表變動(大於10G 以上的)比較頭疼,尤爲是某些 DDL 會鎖表,影響業務可持續性。目前通用的方案使用 Percona 公司開源的 pt-osc 工具解決致使鎖表的操做,還有一款 github 基於 go 語言開發的 gh-ost。本文主要介紹 gh-ost 使用方法,其工做原理放到下一篇文章介紹。html

2、使用

2.1 gh-ost 介紹

gh-ost 做爲一個假裝的備庫,能夠從主庫/備庫上拉取 binlog,過濾以後從新應用到主庫上去,至關於主庫上的增量操做經過 binlog 又應用回主庫自己,不過是應用在幽靈表上。
git

其大體的工做過程:github

  1. gh-ost 首先鏈接到主庫上,根據 alter 語句建立幽靈表,
  2. 而後做爲一個備庫鏈接到其中一個真正的備庫或者主庫上(根據具體的參數來定),一邊在主庫上拷貝已有的數據到幽靈表,一邊從備庫上拉取增量數據的 binlog,而後不斷的把 binlog 應用回主庫。
  3. 等待所有數據同步完成,進行 cut-over 幽靈表和原表切換。圖中 cut-over 是最後一步,鎖住主庫的源表,等待 binlog 應用完畢,而後替換 gh-ost 表爲源表。gh-ost 在執行中,會在本來的 binlog event 裏面增長如下 hint 和心跳包,用來控制整個流程的進度,檢測狀態等。

固然 gh-ost 也會作不少前置的校驗檢查,好比 binlog_format,表的主鍵和惟一鍵,是否有外鍵等等
這種架構帶來諸多好處,例如:算法

整個流程異步執行,對於源表的增量數據操做沒有額外的開銷,高峯期變動業務對性能影響小。
下降寫壓力,觸發器操做都在一個事務內,gh-ost 應用 binlog 是另一個鏈接在作。
可中止,binlog 有位點記錄,若是變動過程發現主庫性能受影響,能夠馬上中止拉binlog,中止應用 binlog,穩定以後繼續應用。
可測試,gh-ost 提供了測試功能,能夠鏈接到一個備庫上直接作 Online DDL,在備庫上觀察變動結果是否正確,再對主庫操做,內心更有底。不過不推薦在備庫直接操做。

2.2 gh-ost 操做模式

a. 鏈接到從庫,在主庫作遷移
這是 gh-ost 默認的工做方式。gh-ost 將會檢查從庫狀態,找到集羣結構中的主庫並鏈接,接下來進行遷移操做:數據庫

  1. 行數據在主庫上讀寫
  2. 讀取從庫的二進制日誌,將變動應用到主庫
  3. 在從庫收集表格式,字段&索引,行數等信息
  4. 在從庫上讀取內部的變動事件(如心跳事件)
  5. 在主庫切換表

若是你的主庫的日誌格式是 SBR,工具也能夠正常工做。但從庫必須啓用二級制日誌( log_bin,log_slave_updates) 而且設置 binlog_format=ROW 。segmentfault

b. 鏈接到主庫
直接鏈接到主庫構造 slave,在主庫上進行 copy 數據和應用 binlog,經過指定 --allow-on-master 參數便可。固然主庫的 binlog 模式必須是 row 模式。服務器

c. 在從庫遷移/測試
該模式會在從庫執行遷移操做。gh-ost 會簡單的鏈接到主庫,此後全部的操做都在從庫執行,不會對主庫進行任何的改動。整個操做過程當中,gh-ost 將控制速度保證從庫能夠及時的進行數據同步架構

--migrate-on-replica 表示 gh-ost 會直接在從庫上進行遷移操做。即便在複製運行階段也能夠進行表的切換操做。
--test-on-replica 表示 遷移操做只是爲了測試在切換以前複製會中止,而後會進行切換操做,而後在切換回來,你的原始表最終仍是原始表。兩個表都會保存下來,複製操做是中止的。你可>以對這兩個表進行一致性檢查等測試操做。

3、實踐

https://github.com/github/gh-ost

3.1 參數說明:

這裏列出比較重要的參數,你們能夠經過以下命令獲取比較詳細的參數以及其解釋。併發

gh-ost --help
-allow-master-master:
是否容許gh-ost運行在雙主複製架構中,通常與-assume-master-host參數一塊兒使用
-allow-nullable-unique-key:
容許gh-ost在數據遷移依賴的惟一鍵能夠爲NULL,默認爲不容許爲NULL的惟一鍵。若是數據遷移(migrate)依賴的惟一鍵容許NULL值,則可能形成數據不正確,請謹慎使用。
-allow-on-master:
容許gh-ost直接運行在主庫上。默認gh-ost鏈接的從庫。
-alter string:
DDL語句
-assume-master-host string:
爲gh-ost指定一個主庫,格式爲」ip:port」或者」hostname:port」。在這主主架構裏比較有用,或則在gh-ost發現不到主的時候有用。
-assume-rbr:
確認gh-ost鏈接的數據庫實例的binlog_format=ROW的狀況下,能夠指定-assume-rbr,這樣能夠禁止從庫上運行stop slave,start slave,執行gh-ost用戶也不須要SUPER權限。
-chunk-size int:
在每次迭代中處理的行數量(容許範圍:100-100000),默認值爲1000。
-concurrent-rowcount:
該參數若是爲True(默認值),則進行row-copy以後,估算統計行數(使用explain select count(*)方式),並調整ETA時間,不然,gh-ost首先預估統計行數,而後開始row-copy。
-conf string:gh-ost的配置文件路徑。
-critical-load string:
一系列逗號分隔的status-name=values組成,當MySQL中status超過對應的values,gh-ost將會退出。-critical-load Threads_connected=20,Connections=1500,指的是當MySQL中的狀態值Threads_connected>20,Connections>1500的時候,gh-ost將會因爲該數據庫嚴重負載而中止並退出。
-critical-load-hibernate-seconds int :
負載達到critical-load時,gh-ost在指定的時間內進入休眠狀態。它不會讀/寫任何來自任何服務器的任何內容。
-critical-load-interval-millis int:
當值爲0時,當達到-critical-load,gh-ost當即退出。當值不爲0時,當達到-critical-load,gh-ost會在-critical-load-interval-millis秒數後,再次進行檢查,再次檢查依舊達到-critical-load,gh-ost將會退出。
-cut-over string:
選擇cut-over類型:
atomic/two-step,atomic(默認)類型的cut-over是github的算法,two-step採用的是facebook-OSC的算法。
-cut-over-exponential-backoff
-cut-over-lock-timeout-seconds int:
gh-ost在cut-over階段最大的鎖等待時間,當鎖超時時,gh-ost的cut-over將重試。(默認值:3)
-database string:
數據庫名稱。
-default-retries int:
各類操做在panick前重試次數。(默認爲60)
-dml-batch-size int:

在單個事務中應用DML事件的批量大小(範圍1-100)(默認值爲10)

-exact-rowcount:

準確統計表行數(使用select count(*)的方式),獲得更準確的預估時間。

-execute:

實際執行alter&migrate表,默認爲noop,不執行,僅僅作測試並退出,若是想要ALTER TABLE語句真正落實到數據庫中去,須要明確指定-execute

-exponential-backoff-max-interval int

-force-named-cut-over:
若是爲true,則'unpostpone | cut-over'交互式命令必須命名遷移的表
-heartbeat-interval-millis int:
gh-ost心跳頻率值,默認爲500
-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失敗。
-max-lag-millis int:
主從複製最大延遲時間,當主從複製延遲時間超過該值後,gh-ost將採起節流(throttle)措施,默認值:1500s。
-max-load string:
逗號分隔狀態名稱=閾值,如:'Threads_running=100,Threads_connected=500'. When status exceeds threshold, app throttles writes
-migrate-on-replica:
gh-ost的數據遷移(migrate)運行在從庫上,而不是主庫上。
-nice-ratio float:
每次chunk時間段的休眠時間,範圍[0.0…100.0]。0:每一個chunk時間段不休眠,即一個chunk接着一個chunk執行;1:每row-copy 1毫秒,則另外休眠1毫秒;0.7:每row-copy 10毫秒,則另外休眠7毫秒。
-ok-to-drop-table:
gh-ost操做結束後,刪除舊錶,默認狀態是不刪除舊錶,會存在_tablename_del表。
-panic-flag-file string:
當這個文件被建立,gh-ost將會當即退出。
-password string :
MySQL密碼
-port int :
MySQL端口,最好用從庫
-postpone-cut-over-flag-file string:
當這個文件存在的時候,gh-ost的cut-over階段將會被推遲,數據仍然在複製,直到該文件被刪除。
-skip-foreign-key-checks:
肯定你的表上沒有外鍵時,設置爲'true',而且但願跳過gh-ost驗證的時間-skip-renamed-columns ALTER
-switch-to-rbr:
讓gh-ost自動將從庫的binlog_format轉換爲ROW格式。
-table string:
表名
-throttle-additional-flag-file string:
當該文件被建立後,gh-ost操做當即中止。該參數能夠用在多個gh-ost同時操做的時候,建立一個文件,讓全部的gh-ost操做中止,或者刪除這個文件,讓全部的gh-ost操做恢復。
-throttle-control-replicas string:
列出全部須要被檢查主從複製延遲的從庫。
-throttle-flag-file string:
當該文件被建立後,gh-ost操做當即中止。該參數適合控制單個gh-ost操做。-throttle-additional-flag-file string適合控制多個gh-ost操做。
-throttle-query string:
節流查詢。每秒鐘執行一次。當返回值=0時不須要節流,當返回值>0時,須要執行節流操做。該查詢會在數據遷移(migrated)服務器上操做,因此請確保該查詢是輕量級的。
-timestamp-old-table:
在舊錶名中使用時間戳。這會使舊錶名稱具備惟一且無衝突的交叉遷移。
-user string :MYSQL用戶

3.2 執行 ddl

測試例子對 test.b 重建表 alter table b engine=innodb;app

/opt/gh-ost/bin/gh-ost \
--max-load=Threads_running=20 \
--critical-load=Threads_running=50 \
--critical-load-interval-millis=5000 \
--chunk-size=1000 \
--user="root" \
--password="" \
--host='127.0.0.1' \
--port=3316 \
--database="test" \
--table="b" \
--verbose \
--alter="engine=innodb" \
--assume-rbr \
--cut-over=default \
--cut-over-lock-timeout-seconds=1 \
--dml-batch-size=10 \
--allow-on-master \
--concurrent-rowcount \
--default-retries=10 \
--heartbeat-interval-millis=2000 \
--panic-flag-file=/tmp/ghost.panic.flag \
--postpone-cut-over-flag-file=/tmp/ghost.postpone.flag \
--timestamp-old-table \
--execute 2>&1 | tee  /tmp/rebuild_t1.log

操做過程當中會生成兩個中間狀態的表 _b_ghc, _b_gho,其中 _b_ghc 是記錄 gh-ost 執行過程的表,其記錄相似以下:

_b_gho 是目標表,也即應用 ddl 語句的幽靈表。

特別說明,上面的命令實際上是在咱們的生產線上直接使用的。通常咱們針對幾百 G 的大表作歸檔刪除數據以後要重建表,以便減小表空間大小。重建完,進行 cut-over 切換幽靈表和原表時,默認不刪除幽靈表。由於直接刪除上百 G 會對磁盤 IO 有必定的影響.
其餘的請各位同行根據本身的狀況去調整合適的參數,注意如下兩個參數。
--ok-to-drop-table:gh-ost操做結束後,刪除舊錶,默認狀態是不刪除舊錶,會存在_tablename_del表。
--timestamp-old-table 最終rename的時候表名會加上時間戳後綴,每次執行的時候都會生成一個新的表名。

3.3 gh-ost 的特性

gh-ost 擁有衆多特性,好比:輕量級、可暫停、可動態控制、可審計、可測試等等,咱們能夠經過操做特定的文件對正在執行的 gh-ost 命令進行動態調整。

暫停/恢復
咱們能夠經過建立/刪除 throttle-additional-flag-file 指定的文件 /tmp/gh-ost.throttle 控制 gh-ost 對 binlog 應用。

限流
gh-ost 能夠經過 unix socket 文件或者 TCP 端口(可配置)的方式來監聽請求,DBA 能夠在命令運行後更改相應的參數,參考下面的例子:
打開限流

echo throttle | socat - /tmp/gh-ost.test.b.sock

_b_ghc 中會多一條記錄

331 | 2019-08-31 23:23:00 | throttle at 1567264980930907070 | done throttling

關閉限流

no-throttle | socat - /tmp/gh-ost.test.b.sock

_b_ghc 中會多一條記錄

347 | 2019-08-31 23:24:09 | throttle at 1567265049830789079 | commanded by user

改變執行參數:chunk-size= 1024, max-lag-millis=100, max-load=Thread_running=23 這些參數均可以在運行時動態調整。

echo chunk-size=1024 | socat - /tmp/gh-ost.test.b.sock
echo max-lag-millis=100 | socat - /tmp/gh-ost.test.b.sock
echo max-load=Thread_running=23 | socat - /tmp/gh-ost.test.b.sock

終止運行
咱們經過來過建立 panic-flag-file 指定的文件,當即終止正在執行的 gh-ostmin
建立文件 /tmp/ghost.panic.flag

gh-ost log提示

2019-08-31 22:50:52.701 FATAL Found panic-file /tmp/ghost.panic.flag. Aborting without cleanup

注意中止 gh-ost 操做會有遺留表 xxx_ghc,xxx_gho還有 socket 文件,管理 cut-over 的文件,若是你須要執行兩次請務必檢查指定目錄是否存在這些文件,而且清理掉文件和表。

4、與 pt-osc 的對比

從功能,穩定性和性能上來看,兩種工具各有千秋,雖然在高併發寫的狀況下,gh-ost 應用 binlog 會出現性能較低不如 pt-osc 的狀況。不過 gh-ost 更靈活,支持咱們根據實際狀況動態調整。
推薦兩個 blog 的文章吧,你們能夠根據本身的實際場景去選擇使用哪一個工具。
https://www.cnblogs.com/zping...
https://blog.csdn.net/poxiaon...

5、總結

整體來說 gh-ost 是一款很是出色的開源產品,感謝 github 爲咱們 MySQL DBA 提供了一種解決大表 ddl 的工具,歡迎還沒使用的朋友試用該工具。

參考文章
https://www.cnblogs.com/zhouj...
https://segmentfault.com/a/11...

相關文章
相關標籤/搜索