[翻译]Triggerless design.md
源文將持續更新,請點擊此處閱讀原文
介紹gh-ost不使用Trigger背后的邏輯和算法,其次是這樣設計的含義,優點,以及缺點。
基于觸發器遷移的理念
這里有兩個比較流行的online DDL工具:
pt-online-schema-change
Facebook OSC
前者使用同步設計原理:它在原表上創建三個觸發器(AFTER INSERT,AFTER UPDATE,AFTER DELETE)。每個觸發器將原表上的操作重播到ghost表中。因此,原表上的每一個UPDATE都會被重播到gh-ost表中,INSERT/DELETE也是如此。觸發器和原表的操作存在與同一個事務空間里。
后者使用異步設計原理:它在原表上創建三個觸發器(AFTER INSERT,AFTER UPDATE,AFTER DELETE)。它也會創建一個changelog表。觸發器不會直接將原表操作重播到ghost表中。取而代之的是,它會在changelog中添加一條記錄。原表上的UPDATE操作在changelog表中插入一條記錄像"原表上有一個UPDATE操作,將這個值變為了另外一個值",INSERT/DELETE也是如此。一個后臺的線程會實時去查看changelog表的變化,并應用所有的變更應用到ghost表上。這是一個異步的操作,因為gh-ost重播操作的過程與原表的操作不在相同的事務空間中了,并且gh-ost的重播操作可能會延遲。值得注意的是,changelog的記錄寫入操作還是與原表操作存在于相同的事務空間中。
不基于觸發器的異步遷移理念
gh-os使用不基于觸發器的異步遷移方案。但是它不需要觸發器,因為它不像FB工具那樣需要一個changelog表。因為它不是基于changelog表做數據遷移,而是基于MySQL二進制日志(binlog)。
gh-ost基于binlog_format=row(RBR)獲取來源于原表的記錄,你也可以基于binlog_format=statement(SBR)獲取記錄,詳情請點擊。
RBR格式的二進制日志將復雜的SQL語句(可能存在多表)分解為不同的單表單行記錄,這樣更易于將binlog應用到gh-ost上。
gh-ost將自己偽裝成MySQL從庫:gh-ost連上主庫,并且從主庫獲取binlog。因此,它獲取binlog日志,并且通過過濾得到原表的操作事件。
gh-ost可以直接連接到主庫上,但是gh-ost更喜歡連接到一個從庫上。但是該從庫需要設置參數log_slave_updates=1和binlog_format=row(binlog_format也可以通過調整gh-ost參數來進行自動設置)。
讀取二進制日志,特別是在在從庫上讀取二進制,進一步強調了算法的異步性。當有一個事物寫入到了binlog中,它依舊需要等gh-ost偽裝成一個從庫之后,才開始獲取binlog并且應用到gh-ost中。
異步設計也意味著有很多值得注意的因素,稍后將做討論。
工作流程概覽
整個工作流程包括:原表上讀取表數據,binlog讀取操作事件,檢查主從延遲以及其它的節流參數,將變更應用到ghost表中(通常是master上的ghost表),通過二進制日志流發送提示等等。
工作流程如下:
初始化設置&驗證
初始化設置不是一個并發操作。
連接從庫(推薦)/主庫,檢查主庫標志
預驗證ALTER語句
初始化驗證:權限以及表是否存在
創建changelog和ghost表
在ghost表上執行ALTER語句
對比原表和ghost表的結構,檢查共享列,共享唯一鍵,驗證是否有外鍵,選擇共享的唯一鍵,這個鍵用于處理表的唯一標識,比如數據遷移等
開始監聽binlog,監聽changelog表的事件
在changelog表上注入"good to go"的記錄(被二進制日志攔截)
開始監聽原表DML的binlog事件
獲取之前原表和ghost表的共享唯一鍵在原表上的最小值和最大值
數據復制流程
該步驟包括多個移動部分,所有操作相互協調并發執行。
設置一個心跳機制:頻繁的寫入到changelog表(這是一個低負載的操作)
心跳機制不斷的更新狀態
定期(頻繁)檢查潛在的節流信息或者提示
在原表上通過行范圍控制,將原表數據拆分為一個chunk一個chunk,并且添加到數據遷移任務隊列中
通過binlog獲取原表的DML語句,并且添加到binlog重播任務隊列中
處理數據遷移任務隊列和binlog重播任務隊列,并將其順序的應用到ghost表上(當遇到節流操作或者hint提示時,將會暫停該操作)
當數據遷移與binlog重播完成后,將會在changelog表上注入/攔截"copy all done"的記錄
當-postpone-cut-over-flag-file參數設置的文件存在時,將會推遲接下來的cut-over操作(但是原表的DML操作依舊會通過binlog應用到ghost表上)
結束操作:交換表流程
將原表加上寫鎖,binlog事件會繼續應用在gh-ost上(該步驟是個異步操作,因此即使表被鎖住,gh-ost仍然可以處理隊列中未處理完的binlog事件)
-
將原表rename為_tablename_del表,ghost則rename為tablename表
rename /* gh-ost */ table `wing`.`t` to `wing`.`_t_del`, `wing`.`_t_gho` to `wing`.`t` 清理工作:刪除需要被清除的表
異步設計優點
Cut-over階段
異步設計最復雜的地方在于cut-over階段:原表和ghost表的交換。在同步設計中,由于原表操作與觸發器操作是在同一個事物空間中的,所以原表和ghost表的數據始終是同步的,因此一個簡單的原表和ghost表交換(Rename)是可以存在的。
在異步設計中,即使我們對原表加鎖,管道中仍然會存在一些事件,binlog日志依舊會將來自原表的事件重播到ghost表上。采用同步設計中交換表的方式是不可取的,因為這意味著,即使還沒有對來自原表的事件重播完畢,就開始使用重命名后的ghost表了,這將造成數據不一致。
Facebook的使用"中斷機制",二步重命名法:
鎖住原表,處理積壓的事件(backlog)
將原表重命名
將ghost重命名為原表的名字
在兩個表交換的時候,會有一個表不存在的階段,因此會存在"表中斷"的問題。
gh-ost通過"two-step"算法解決這個問題,"two-step"算法會阻塞表寫入,然后將兩表交換。它使操作很安全,要么成功,要么失敗則回滾到cut-over階段之前。
更多的信息請閱讀cut-over
分離去耦
不使用觸發器的異步設計最大的影響在于工作負載的去耦。使用觸發器的設計,不管是同步還是異步方法,原表上的每一個寫入意味著需要立刻在另外一張表上寫入。
We will break down the meaning of workload decoupling, shortly. But it is important to understand that gh-ost interprets the situation in its own time and acts in its own time, yet still makes this an online operation.
The decoupling is important not only as the tool's logic goes, but very importantly as the master server sees it. As far as the master knows, write to the table and writes to the ghost table are unrelated
寫負載
不使用觸發器意味著主庫不需要有過多的寫負載用在存儲過程上,以及對gh-ost表的鎖爭用上。
將原表操作重播到ghost表上完全由gh-ost工具完成。因此gh-ost可以決定何時將數據寫入到ghost表中。為了分解原表的寫負載,gh-ost工具選擇使用一個單獨的線程將原表操作重播到ghost表上。
MySQL對一個表大量并發寫入的時候性能不是很好,這個時候鎖變成了一個很大的問題。This is why we choose to alternate between the massive row-copy and the ongoing binlog events backlog such that the server only sees writes from a single connection。
更有趣的是,gh-ost是唯一寫入到ghost表的程序,沒有人意識到ghost表的存在。因此,gh-ost工具不存在由觸發器產生的高并發性問題以及資源高爭用問題。
可暫停性
當gh-ost暫停工作(節流導致),此時將會沒有任何數據寫入到ghost表中。因為gh-ost不存在觸發器,寫負載是與gh-ost寫負載分開的。并且由于gh-ost使用異步設計的方法,gh-ost算法已經處理了主庫寫入與ghost表寫入的時間差。對于gh-ost來說,幾毫秒的時間差與幾小時的時間差并沒有任何區別,對于gh-ost的運行并沒有任何的影響。
當gh-ost進行節流操作的時候,不管是因為主從復制延遲,還是達到max-load設置閥值等,原表還是正常操作。僅僅只是對ghost表沒有任何的寫操作。除了changelog表上的heartbeat在不斷的更新,但這個操作帶來的性能影響是可以忽略不計的。
可測試性
我們甚至可以測試數據遷移(migration)步驟:就像我們將數據遷移的操作與主庫的負載分開一樣,我們不在主庫上應用所有的變更,我們選擇一個從庫,這樣我們可以在從庫上進行表的數據遷移(migration)。
這本身是一個很不錯的功能;它為我們提供了測試的可能性:正如我們完成數據遷移一樣,我們從庫的復制(stop slave)。gh-ost會進行cut-over階段,但是gh-ost也會回滾回去。gh-ost測試時不會刪除任何的表。結果是從庫上的原表和ghost表都會存在,也不會做進一步的變更操作(因為此時主從復制已經停止)。我們可以對兩張表進行對比。
這個方法可以用于驗證gh-ost工具的正確性:在多個生產從庫上不斷的重復的做"數據遷移"(實際上并不會修改列)。每次數據遷移后都對原表和ghost表做一次數據校驗。gh-ost預計是所有的表數據校驗都是一致的。
多表并發操作
gh-ost可以運行多個不同的表并發操作(當然不是在相同的表上并發操作)。gh-ost異步設計的方法是支持多個不同的表并發操作的。事實上沒有觸發器的存在,多個不同的表并發操作gh-ost對于主庫來說,只是并發的多個連接而已。每個gh-ost都可以控制自己的節流,或者全部一次性控制它們的節流操作。
Going outside the server space
More to come as we make progress
異步設計缺點
增加流量
現有的工具都是通過觸發器來重播原表上的操作。gh-ost是通過自己來獲取原表操作,然后重播到gh-ost表上。gh-ost當然更喜歡在從庫上獲取原表操作,然后在主庫上重播到gh-ost表上。這也意味著,主庫機器和從庫機器之間存在數據的傳輸。并且gh-ost使用的MySQL客戶端不支持壓縮功能, and so during a migration you can expect the full volume of a table to transfer on the wire。
增加代碼復雜性
基于觸發器同步方法的online DDL工具相對來說代碼較少。大量的數據遷移是基于觸發器完成的?;貪L,數據類型以及cut-over階段都是由數據庫隱式處理的。gh-ost的異步方法讓它的代碼變的更復雜。它分別連接到主庫和從庫,偽裝成從庫,向主庫寫入心跳事件,在從庫上讀取binlog事件并寫入到主庫上,它還需要管理連接失敗,主從復制延遲等等。
因此,gh-ost擁有更大的代碼庫以及更復雜的異步并發邏輯。
總結
以上是生活随笔為你收集整理的[翻译]Triggerless design.md的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【NOIP】提高组2012 同余方程
- 下一篇: 移除单链表的倒数第N个节点