gh-ost —— GitHub Online DDL 工具使用詳解

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

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.使用

  • 詳細說明都在安全

    cheatsheetbash

    。您可能對以各類模式調用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 默認的工做模式,它會查看從庫狀況,找到集羣的主庫而且鏈接上去。修改操做的具體步驟是app

    • 在主庫上讀寫行數據;
    • 在從庫上讀取二進制日誌事件,將變動應用到主庫上;
    • 在從庫上查看錶格式、字段、主鍵、總行數等;
    • 在從庫上讀取 gh-ost 內部事件日誌(好比心跳);
    • 在主庫上完成表切換;
  • 若是主庫的二進制日誌格式是 statement,就可使用這種模式。但從庫就必須配成啓用二進制日誌(log_bin, log_slave_updates),還要設成 Row 格式(binlog_format=ROW),實際上 gh-ost 會在從庫上幫你作這些設置。
  • 事實上,即便把從庫改爲 Row 格式,這仍然是對主庫侵入最少的工做模式。less

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 操做模式,上面的操做中,到最後不會清理臨時表,須要手動清理,再下次執行以前果真臨時表還存在,則會執行失敗,能夠經過參數進行刪除:
  • 還有其餘的一些參數,好比:--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.更多

相關文章
相關標籤/搜索