gh-ost工具線上改表過程的詳細解析
gh-ost,是github開源的一款線上修改MySQL表結構的工具https://github.com/github/gh-ost/,它不使用pt-osc的觸發器機制,而是使用解析binlog來實現將增量資料複製到新表。
最近我抽空了解了一下它的原始碼,結合debug日誌,整理出它的過程,如下:
1.檢查過程:
a.連線驗證:connection validated on...
b.檢查使用者許可權:User has SUPER, REPLICATION SLAVE privileges, and has ALL privileges on `sbtest`.*
c.binlog驗證:binary logs validated on...
d.檢查表儲存引擎及評估表資料量:Estimated number of rows via EXPLAIN
e.判斷Master節點
2.準備過程
a.偽裝成Slave節點,通過binlogsyncer連線到Master節點,拉取binlog
b.初始化Applier,Applier的用途是apply增量binlog至gho表,apply的方式,上面有介紹過。
c.建立ghc(change log表),建立gho(影子表),將alter語句應用到gho表
d.選擇可以確定唯一行的欄位,確定目標表的所有欄位(Shared columns)
e.建立一個socket檔案,用來執行如暫停、限流等操作,參考https://github.com/github/gh-ost/blob/master/doc/interactive-commands.md。
f.根據命令指定的引數,決定使用何種方式來計算表的資料量,並通過上面選擇的唯一鍵來確定最大、最小值
g.監視throttle-additional-flag-file,以決定是否限流
3.資料同步過程
a.開始copy源表資料至gho表,示例語句:
insert /* gh-ost `sbtest`.`t1` */ ignore into `sbtest`.`_t1_gho` (`id`)
(select `id` from `sbtest`.`t1` force index (`PRIMARY`)
where (((`id` > _binary'1') or ((`id` = _binary'1'))) and ((`id` < _binary'4') or ((`id` = _binary'4')))) lock in share mode
b.同時,將binlog中與源表相關的dml操作,在gho表上面應用,以保持2表的資料一致。 在本文的最後部分會有較詳細的分析。
4.交換表過程
gh-ost實現原子交換過程的邏輯,都在migrator.go的atomicCutOver()函式中,過程如下:
a.Copy執行緒使用select get_lock('gh-ost.989.lock', 0)方式建立鎖,以確保ghost工具各執行緒之間的操作一致,其中989是Copy執行緒連線到MySQL的connection_id
b.Rename執行緒建立一個magic cut-over table,表名是源表名加上_del字尾。至於magic cut-over table名稱的由來,是為了防止在rename操作成功之前,Copy執行緒意外斷開與MySQL的連線,從而導致新資料被寫入到源表,而這部分新資料無法同步到gho表。
c.Copy執行緒同時鎖住源表和_del表
d.Rename執行緒等待所有的event在gho表上應用完畢
e.Rename執行緒交換表,此時因為表被Copy執行緒鎖住,所以此時會一直等待鎖釋放,示例語句:rename /* gh-ost */ table `sbtest`.`t1` to `sbtest`.`_t1_del`, `sbtest`.`_t1_gho` to `sbtest`.`t1`
f.另外一個執行緒使用select is_used_lock('gh-ost.989.lock')方式判斷Copy執行緒是否持有鎖,因為必須要確保有鎖,才能防止源表被寫入新資料
g.Copy執行緒現在可以安全地同刪除_del表了,然後unlock tables, 並rollback
h.上述步驟完成的同時,Rename執行緒獲取鎖,完成原子rename操作
交換表的過程,我粗略整理了一下流程如下:
5.收尾工作
刪除ghc字尾的changelog表
ost期間,增量資料如何複製?
這是gh-ost的特點之一。實現原理如下:
osc期間,源表新增的資料,是通過解析binlog中的DML Events,然後由logic/applier.go的buildDMLEventQuery呼叫sql/builder.go中相應的函式(例如insert操作對應的是BuildDMLInsertQuery函式),來生成相應的query語句,然後返回給logic/applier.go的ApplyDMLEventQueries(tx.Exec)執行,以實現同步資料到gho表。