[原創]gh-ost —— GitHub Online DDL 工具使用詳解


GitHub’s online schema migration for MySQL
項目地址:gh-ost

1.簡介

  • gh-ost是一個無觸發器的MySQL schema在線遷移解決方案。它是可測試的,並提供了可用性、動態控制/重新配置、審計和許多操作功能。
  • 在整個遷移過程中,gh-ost在主庫上產生的工作負載較輕,與遷移表上的現有工作負載解耦。
  • 它是根據多年使用現有解決方案的經驗設計的,並更改了表遷移的范例。

2.為什么不用觸發器 ?

  • 已存在的工具有:
    • pt-osc
    • Facebook OSC
    • LHM
    • oak-online-alter-table
  • 它們都使用觸發器將表上的活動,傳輸更新到正在緩慢同步的鬼/影表。這些工具的工作方式不盡相同:大多數工具使用同步方法(應用於ghost表的所有更改),而Facebook工具使用異步方法(將更改追加到更改日志表,稍后將審查並應用於ghost表)。
  • 觸發器的使用簡化了動態表遷移中的許多流程,但也帶來了一些限制或困難。以下是我們選擇為模式遷移設計無觸發解決方案design a triggerless solution的原因。

3.命名由來

  • 最初它被命名為gh-osc: GitHub Online Schema Change,類似於Facebook online schema change和pt-online-schema-change。

    但后來發生了一種罕見的基因突變,c變成了t。這讓我們走上了尋找一個新的縮略詞的道路。gh-ost(發音:Ghost),代表GitHub的在線模式轉換器/變形器。

4.亮點

  • 通過在從庫上測試gh-ost來建立您對它的信任。gh-ost將發出與主表相同的流,遷移復制表上的表,而不實際替換原始表,將復制表保留為兩個表,然后您可以比較並確信該工具操作正確。這就是我們在生產中不斷測試gh-ost的方法。
  • 真正的暫停:當gh-ost停止throttles時,它真正停止對master上的寫:沒有行復制,也沒有正在進行的事件處理。通過節流,可以將主服務器返回到其原始工作負載
  • 動態控制:即使遷移仍在運行,您也可以交互式interactively地重新配置gh-ost。您可以強制啟動節流
  • 審計:您可以查詢gh-ost的狀態。在unix套接字或TCP上監聽gh-ost
  • 切換階段的控制:可以指示gh-ost推遲可能是最關鍵的步驟:表的交換,直到您可以方便地使用為止。沒有必要擔心ETA在辦公時間之外
  • 外部掛載hooks可以將gh-ost與您的特定環境結合起來

5.使用

  • 詳細說明都在

    cheatsheet

    。您可能對以各種模式調用gh-ost感興趣:

    • noop遷移(僅僅測試遷移是有效的和良好的)
    • 使用從庫進行的真正遷移(如果您的主庫使用基於語句的復制,則需要在主庫上運行;gh-ost可以識別涉及的服務器的身份。)
    • 一個真正的遷移,直接在主庫上運行(但是gh-ost更喜歡前者)
    • 在從庫上做真實遷移(主版本未受影響)
    • 在從庫上做測試遷移,您可以通過這種方式與gh-ost的操作建立信任。

6.它是如何工作的?

  • 所有現有的在線模式變更工具以類似的方式操作:他們創建一個與你原始表相似的ghost表,緩慢而逐步將數據從原始表復制到ghost表,同時不斷傳輸增量 (任何insert、delete、update應用到你的表)到ghost表。
  • 最后,在適當的時候,它們會用ghost表替換原來的表。
  • gh-ost使用相同的模式。但是,它不同於所有現有的工具,不使用觸發器。我們已經認識到觸發器是許多限制和風險的根源many limitations and risks
  • 相反,gh-ost使用binlog流捕獲表更改uses the binary log stream,並異步地將它們應用到ghost表。gh-ost承擔了一些其他工具留給數據庫執行的任務。因此,gh-ost對遷移過程有更大的控制:可以真正暫停、可以真正地將遷移的寫負載與主工作負載分離。
  • 此外,它還提供了許多操作上的好處operational perks,使它更安全、更值得信賴、使用起來更有趣。

gh-ost 首先連接到主庫上,根據 alter 語句創建幽靈表,然后作為一個”備庫“連接到其中一個真正的備庫上,一邊在主庫上拷貝已有的數據到幽靈表,一邊從備庫上拉取增量數據的 binlog,然后不斷的把 binlog 應用回主庫。圖中 cut-over 是最后一步,鎖住主庫的源表,等待 binlog 應用完畢,然后替換 gh-ost 表為源表。gh-ost 在執行中,會在原本的 binlog event 里面增加以下 hint 和心跳包,用來控制整個流程的進度,檢測狀態等。這種架構帶來諸多好處,例如:

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

7.工作模式

  • gh-ost 根據參數配置可選三種變更模式
  • 除了直連主庫和連接從庫以外,還有連接從庫做變更測試

7.1.模式1 —— 連上從庫,在主庫上修改

  • 這是 gh-ost 默認的工作模式,它會查看從庫情況,找到集群的主庫並且連接上去。修改操作的具體步驟是

    • 在主庫上讀寫行數據;
    • 在從庫上讀取二進制日志事件,將變更應用到主庫上;
    • 在從庫上查看表格式、字段、主鍵、總行數等;
    • 在從庫上讀取 gh-ost 內部事件日志(比如心跳);
    • 在主庫上完成表切換;
  • 如果主庫的二進制日志格式是 statement,就可以使用這種模式。但從庫就必須配成啟用二進制日志(log_bin, log_slave_updates),還要設成 Row 格式(binlog_format=ROW),實際上 gh-ost 會在從庫上幫你做這些設置。

  • 事實上,即使把從庫改成 Row 格式,這仍然是對主庫侵入最少的工作模式。

7.2.模式2 —— 直接在主庫上修改

  • 如果沒有從庫,或者不想在從庫上操作,那直接用主庫也是可以的。gh-ost 就會在主庫上直接做所有的操作。仍然可以在上面查看主從復制延遲。

    • 主庫必須產生 Row 格式的二進制日志;
    • 啟動 gh-ost 時必須用 --allow-on-master 選項來開啟這種模式;

7.3.模式3 —— 在從庫上修改和測試

  • 這種模式會在從庫上做修改。gh-ost 仍然會連上主庫,但所有操作都是在從庫上做的,不會對主庫產生任何影響。在操作過程中,gh-ost 也會不時地暫停,以便從庫的數據可以保持最新。

    • --migrate-on-replica 選項讓 gh-ost 直接在從庫上修改表。最終的切換過程也是在從庫正常復制的狀態下完成的。
    • --test-on-replica 表明操作只是為了測試目的。在進行最終的切換操作之前,復制會被停止。原始表和臨時表會相互切換,再切換回來,最終相當於原始表沒被動過。主從復制暫停的狀態下,你可以檢查和對比這兩張表中的數據。

8.下載

9.參數說明

option meaning default value
--aliyun-rds 是否在阿里雲數據庫上執行 true
--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語句
--approve-renamed-columns ALTER 如果你修改一個列的名字,gh-ost將會識別到並且需要提供重命名列名的原因,默認情況下gh-ost是不繼續執行的,除非提供-approve-renamed-columns ALTER
--ask-pass MySQL密碼
--assume-master-host 為gh-ost指定一個主庫,格式為”ip:port”或者”hostname:port”。在這主主架構里比較有用,或則在gh-ost發現不到主的時候有用
--assume-rbr 確認gh-ost連接的數據庫實例的binlog_format=ROW的情況下,可以指定-assume-rbr,這樣可以禁止從庫上運行stop slave,start slave,執行gh-ost用戶也不需要SUPER權限
--check-flag
--chunk-size 在每次迭代中處理的行數量(允許范圍:100-100000) 1000
--concurrent-rowcount 該參數如果為True,則進行row-copy之后,估算統計行數(使用explain select count(*)方式),並調整ETA時間,否則,gh-ost首先預估統計行數,然后開始row-copy true
--conf 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 負載達到critical-load時,gh-ost在指定的時間內進入休眠狀態。 它不會讀/寫任何來自任何服務器的任何內容
--critical-load-interval-millis 當值為0時,當達到-critical-load,gh-ost立即退出。當值不為0時,當達到-critical-load,gh-ost會在-critical-load-interval-millis秒數后,再次進行檢查,再次檢查依舊達到-critical-load,gh-ost將會退出
--cut-over 選擇cut-over類型:atomic/two-step,atomic(默認)類型的cut-over是github的算法,two-step采用的是facebook-OSC的算法
--cut-over-exponential-backoff
--cut-over-lock-timeout-seconds gh-ost在cut-over階段最大的鎖等待時間,當鎖超時時,gh-ost的cut-over將重試 3
--database string 數據庫名
--debug debug模式
--default-retries 各種操作在panick前重試次數 60
--discard-foreign-keys 該參數針對一個有外鍵的表,在gh-ost創建ghost表時,並不會為ghost表創建外鍵。該參數很適合用於刪除外鍵,除此之外,請謹慎使用
--dml-batch-size 在單個事務中應用DML事件的批量大小(范圍1-100) 1
--exact-rowcount 准確統計表行數(使用select count(*)的方式),得到更准確的預估時間
--execute 實際執行alter&migrate表,默認為noop,不執行,僅僅做測試並退出,如果想要ALTER TABLE語句真正落實到數據庫中去,需要明確指定-execute
--exponential-backoff-max-interval
--force-named-cut-over 如果為true,則unpostpone / cut-over交互式命令必須命名遷移的表
--force-table-names 在臨時表上使用的表名前綴
--heartbeat-interval-millis gh-ost心跳頻率值 500
--hooks-hint 任意消息通過 GH_OST_HOOKS_HINT 注入到 hook
--hooks-path hook文件存放目錄(默認為empty,即禁用hook)。hook會在這個目錄下尋找符合約定命名的hook文件來執行
--host MySQL IP/hostname
--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失敗
--master-password MySQL 主密碼
--master-user MysQL主賬號
--max-lag-millis 主從復制最大延遲時間,當主從復制延遲時間超過該值后,gh-ost將采取節流(throttle)措施 1500s
--max-load 逗號分隔狀態名稱=閾值,如:'Threads_running=100,Threads_connected=500'. When status exceeds threshold, app throttles writes
--migrate-on-replica gh-ost的數據遷移(migrate)運行在從庫上,而不是主庫上
--nice-ratio 每次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 當這個文件被創建,gh-ost將會立即退出
--password MySQL密碼
--port MySQL端口,最好用從庫
--postpone-cut-over-flag-file 當這個文件存在的時候,gh-ost的cut-over階段將會被推遲,數據仍然在復制,直到該文件被刪除
--quiet 靜默模式
--replica-server-id gh-ost的server_id
--replication-lag-query 棄用
--serve-socket-file gh-ost的socket文件絕對路徑
--serve-tcp-port gh-ost使用端口,默認為關閉端口
--skip-foreign-key-checks 確定你的表上沒有外鍵時,設置為'true',並且希望跳過gh-ost驗證的時間-skip-renamed-columns ALTER
--skip-renamed-columns ALTER 如果你修改一個列的名字(如change column),gh-ost將會識別到並且需要提供重命名列名的原因,默認情況下gh-ost是不繼續執行的。該參數告訴gh-ost跳該列的數據遷移,讓gh-ost把重命名列作為無關緊要的列。該操作很危險,你會損失該列的所有值
--stack 添加錯誤堆棧追蹤
--switch-to-rbr 讓gh-ost自動將從庫的binlog_format轉換為ROW格式
--table 表名
--test-on-replica 在從庫上測試gh-ost,包括在從庫上數據遷移(migration),數據遷移完成后stop slave,原表和ghost表立刻交換而后立刻交換回來。繼續保持stop slave,使你可以對比兩張表
--test-on-replica-skip-replica-stop 當-test-on-replica執行時,該參數表示該過程中不用stop slave
--throttle-additional-flag-file 當該文件被創建后,gh-ost操作立即停止。該參數可以用在多個gh-ost同時操作的時候,創建一個文件,讓所有的gh-ost操作停止,或者刪除這個文件,讓所有的gh-ost操作恢復
--throttle-control-replicas 列出所有需要被檢查主從復制延遲的從庫
--throttle-flag-file 當該文件被創建后,gh-ost操作立即停止。該參數適合控制單個gh-ost操作。-throttle-additional-flag-file string適合控制多個gh-ost操作
--throttle-http
--throttle-query string 節流查詢。每秒鍾執行一次。當返回值=0時不需要節流,當返回值>0時,需要執行節流操作。該查詢會在數據遷移(migrated)服務器上操作,所以請確保該查詢是輕量級的
--timestamp-old-table 在舊表名中使用時間戳。 這會使舊表名稱具有唯一且無沖突的交叉遷移
--tungsten 告訴gh-ost你正在運行的是一個tungsten-replication拓撲結構
--user MYSQL用戶
--verbose
--version

10.實際操作

  • 使用說明:條件是操作的MySQL上需要的binlog模式是ROW。如果在一個從上測試也必須是ROW模式,還要開啟log_slave_updates。根據上面的參數說明按照需求進行調整
  • 環境:主庫:192.168.1.101;從庫:192.168.1.102

10.1. DDL執行過程

  • 校驗階段:
    • 檢查有沒有外鍵和觸發器。
    • 檢查表的主鍵信息。
    • 檢查是否主庫或從庫,是否開啟 log_slave_updates,以及 binlog 信息
    • 檢查 gho 和 del 結尾的臨時表是否存在
    • 創建 ghc 結尾的表,存數據遷移的信息,以及 binlog 信息等
  • 初始化 stream 的連接,添加 binlog 的監聽
  • 遷移階段:
    • 創建 gho 結尾的臨時表,執行 DDL 在 gho 結尾的臨時表上
    • 開啟事務,按照主鍵id把源表數據寫入到 gho 結尾的表上,再提交,以及binlog apply
  • cut-over 階段:
    • lock 源表,rename 表:rename 源表 to 源 _del 表,gho 表 to 源表
    • 清理 ghc 表

10.1.1. 單實例上DDL

  • 單個實例相當於主庫,需要開啟--allow-on-master參數和ROW模式
gh-ost --user="root" --password="root" --host=192.168.1.101  --database="test" --table="t1" 
 --alter="ADD COLUMN cc2 varchar(10),add column cc3 int not null default 0 comment 'test' " --allow-on-master  --execute

10.1.2. 主從上DDL

  • 有2個選擇,一是按照1直接在主上執行同步到從上,另一個連接到從庫,在主庫做遷移(只要保證從庫的binlog為ROW即可,主庫不需要保證)
gh-ost --user="root" --password="root" --host=192.168.1.102  --database="test" --table="t" --initially-drop-old-table
 --alter="ADD COLUMN y1 varchar(10),add column y2 int not null default 0 comment 'test' "  --execute
  • 此時的操作大致是:
    • 行數據在主庫上讀寫
    • 讀取從庫的二進制日志,將變更應用到主庫
    • 在從庫收集表格式,字段&索引,行數等信息
    • 在從庫上讀取內部的變更事件(如心跳事件)
    • 在主庫切換表
  • 在執行DDL中,從庫會執行一次 stop/start slave,要是確定從的 binlog 是 ROW 的話可以添加參數:--assume-rbr。如果從庫的 binlog 不是 ROW,可以用參數 --switch-to-rbr 來轉換成 ROW,此時需要注意的是執行完畢之后,binlog 模式不會被轉換成原來的值。--assume-rbr 和 --switch-to-rbr 參數不能一起使用。

10.1.3.在從上進行DDL測試

gh-ost --user="root" --password="root" --host=192.168.1.102  --database="test" --table="t" 
 --alter="ADD COLUMN abc1 varchar(10),add column abc2 int not null default 0 comment 'test' " --test-on-replica  --switch-to-rbr --execute
  • 參數--test-on-replica:在從庫上測試gh-ost,包括在從庫上數據遷移 (migration),數據遷移完成后 stop slave,原表和 ghost 表立刻交換而后立刻交換回來。繼續保持 stop slave,使你可以對比兩張表。如果不想 stop slave,則可以再添加參數:--test-on-replica-skip-replica-stop

  • 上面三種是 gh-ost 操作模式,上面的操作中,到最后不會清理臨時表,需要手動清理,再下次執行之前果然臨時表還存在,則會執行失敗,可以通過參數進行刪除:

    • --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表。
  • 還有其他的一些參數,比如:--exact-rowcount、--max-lag-millis、--max-load 等等,可以看上面的說明,具體大部分常用的參數命令如下:

gh-osc --user= --password= --host= --database= --table= --max-load=Threads_running=30, --chunk-size=1000 --serve-socket-file=/tmp/gh-ost.test.sock
 --exact-rowcount --allow-on-master/--test-on-replica --initially-drop-ghost-table/--initially-drop-old-table/--initially-drop-socket-file
 --max-lag-millis= --max-load='Threads_running=100,Threads_connected=500' --ok-to-drop-table

10.1.4.額外說明:終止、暫停、限速

gh-ost --user="root" --password="root" --host=192.168.1.101  --database="test" --table="t1" 
 --alter="ADD COLUMN o2 varchar(10),add column o1 int not null default 0 comment 'test' " --exact-rowcount
 --serve-socket-file=/tmp/gh-ost.t1.sock --panic-flag-file=/tmp/gh-ost.panic.t1.flag  --postpone-cut-over-flag-file=/tmp/ghost.postpone.t1.flag
 --allow-on-master  --execute
  • 表示文件終止運行--panic-flag-file
    • 創建文件終止運行,例子中創建 /tmp/gh-ost.panic.t1.flag 文件,終止正在運行的 gh-ost,臨時文件清理需要手動進行
  • 表示文件禁止cut-over進行,即禁止表名切換,數據復制正常進行。--postpone-cut-over-flag-file
    • 創建文件延遲cut-over進行,即推遲切換操作。例子中創建/tmp/ghost.postpone.t1.flag文件,gh-ost 會完成行復制,但並不會切換表,它會持續的將原表的數據更新操作同步到臨時表中。
  • 使用socket監聽請求,操作者可以在命令運行后更改相應的參數。--serve-socket-file,--serve-tcp-port(默認關閉)
  • 創建socket文件進行監聽,通過接口進行參數調整,當執行操作的過程中發現負載、延遲上升了,不得不終止操作,重新配置參數,如 chunk-size,然后重新執行操作命令,可以通過scoket接口進行動態調整。如:

10.1.4.1.暫停操作:

#暫停
echo throttle | socat - /tmp/gh-ost.test.t1.sock
#恢復
echo no-throttle | socat - /tmp/gh-ost.test.t1.sock

10.1.4.2.修改限速參數:

echo chunk-size=100 | socat - /tmp/gh-ost.t1.sock
echo max-lag-millis=200 | socat - /tmp/gh-ost.t1.sock
echo max-load=Thread_running=3 | socat - /tmp/gh-ost.t1.sock

11.建議

  • Testing above all,嘗試 --test-on-replica前幾次。更好的是,讓它繼續運行。我們有多個從庫,在這些從庫中,我們迭代所有的生產表,逐個遷移它們,檢查和結果,驗證遷移是好的。
  • 對於每個主庫遷移,首先發出一個noop
  • 然后通過——問題的執行

12.更多的小貼士

  • 使用 --exact-rowcount獲取精確的提示
  • 使用 --postpone-cut-over-flag-file控制切換時機
  • 熟悉交互式命令interactive commands

13.更多


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM