本月初,GitHub數(shù)據(jù)庫基礎(chǔ)架構(gòu)組的高級軟件工程師Shlomi Noach在GitHub Engineering網(wǎng)站上發(fā)文宣告了gh-ost的開源發(fā)布。這對MySQL社區(qū)是一件大事,宣告停滯許久的MySQL表在線修改表定義操作又有了新的解決方案。
Shlomi這樣總結(jié):
gh-ost是GitHub最近幾個月開發(fā)出來的,目的是解決一個經(jīng)常碰到的問題:不斷變化的產(chǎn)品需求會不斷要求更改MySQL表結(jié)構(gòu)。gh-ost通過一種影響小、可控制、可審計、操作簡單而且安全的方式來改變線上表結(jié)構(gòu)。
目前,MySQL在線修改表定義的任務(wù)主要是通過這三種途徑完成的:
在從庫上修改表定義,修改之后再提升為新的主庫。 通過MySQL 5.6開始提供的InnoDB在線DDL功能。 使用修改表定義工具?,F(xiàn)在最流行的是Percona公司的pt-online-schema-change和Facebook的OSC,也有人使用LHM或最早的oak-online-alter-table。在從庫上修改表定義的方案耗時長,需要更多的服務(wù)器,這種方案需要非常細(xì)致的管理工作,并且修改完畢后主從切換過程也會造成短暫的停服。InnoDB在線DDL功能在主庫上操作的確是可以滿足需求的,但過程不可中斷,并且操作完成會造成主從庫之間較大的延遲,對許多高可用方案及讀寫分離等會造成較大困擾。被各大公司DBA廣泛接受的作法主要是通過Percona公司的pt-online-schema-change和Facebook的OSC在線操作。
所有在線更改表定義的工具運行原理都是相似的:創(chuàng)建一張與原始表定義相同的臨時表,趁上面沒有數(shù)據(jù)時先把臨時表改好表定義,然后慢慢地、用增量方式把數(shù)據(jù)從原始表拷到臨時表,同時不斷地把進(jìn)行中的原始表上的數(shù)據(jù)操作(所有應(yīng)用在原始表上的插入、刪除、更新操作)也應(yīng)用過來。當(dāng)工具把所有數(shù)據(jù)都拷貝完畢,兩邊數(shù)據(jù)同步了之后,它就用這張臨時表來替代原始表。修改過程就結(jié)束了。
像pt-online-schema-change、LHM和oak-online-alter-table這些工具用的都是同步復(fù)制的方式,對表的每一條數(shù)據(jù)修改都會立刻在同一個事務(wù)里就應(yīng)用到臨時表上。Facebook的OSC工具用的則是異步模式,先把修改操作都記在一張修改日志表里,然后再取出來執(zhí)行,把修改操作應(yīng)用到臨時表上。這些工具全都使用觸發(fā)器來提取那些應(yīng)用在目標(biāo)表上的操作。
除了在修改過程中的性能、運維等問題之外,在最后的表切換——用臨時表替代原始表——的過程中也非常容易出各種問題:切換失敗、丟數(shù)據(jù)、阻塞原始表業(yè)務(wù)操作過久等。為此Shlomi曾在博客上基于較優(yōu)的Facebook OSC的行為連續(xù)寫了三篇文章(一、二、三)進(jìn)行分析,進(jìn)行了細(xì)致討論,并最終給出了一種比較簡單而又無損的方案。
GitHub每天數(shù)據(jù)庫基礎(chǔ)架構(gòu)組幾乎每天都會收到幾次對表結(jié)構(gòu)進(jìn)行修改的請求,因此各種可能的丟失數(shù)據(jù)、復(fù)雜管理、進(jìn)度不可見等行為都是不可忍受的。最終他們經(jīng)過周密的討論、研發(fā)和測試,推出了自己的工具:gh-ost,這個名字是gitHub's Online Schema Transmogrifier/Transfigurator/Transformer/Thingy的縮寫,讀音同Ghost。
gh-ost有以下特點:
無觸發(fā)器:這也是其他工具最受詬病之處。觸發(fā)器方案會對MySQL的性能造成比較大的影響,嚴(yán)重時甚至?xí)峡逯鲙臁?輕量級:gh-ost獲取數(shù)據(jù)表修改操作的方法是偽裝成從庫連入,獲取并解析二進(jìn)制日志,對臨時表插入數(shù)據(jù)也是增量、可控制的,因此對MySQL主庫的性能幾乎無影響。 可暫停:當(dāng)原主庫處于業(yè)務(wù)高峰期時,完全可以暫停gh-ost的操作,暫停就意味著對主庫沒有寫入和更新,這是非常受歡迎的。 動態(tài)可控:gh-ost的操作不但可以暫停,還可以動態(tài)修改,因此在各種情況下修改了配置之后都不必從頭開始重新運行整個修改過程,這是非常節(jié)約資源的。 可審計:gh-ost的狀態(tài)是可以非常容易獲取到的,包括當(dāng)前任務(wù)進(jìn)度、主要配置參數(shù)、相關(guān)MySQL實例的情況等。gh-ost通過監(jiān)聽TCP或者unix socket文件來獲取命令,因此就給了運維人員極大的靈活性。 可測試:gh-ost支持在從庫上進(jìn)行測試,以觀察對系統(tǒng)負(fù)載的影響、驗證正確性等。GitHub生產(chǎn)環(huán)境的每一張表都這樣用gh-ost在從庫上做過好多次修改測試,他們也呼吁大家用這種方式先體驗gh-ost的功能,再考慮上線應(yīng)用。 可靠性高:經(jīng)過充分的測試之后,現(xiàn)在GitHub生產(chǎn)環(huán)境的修改表定義操作已經(jīng)全部由gh-ost完成了,而且它還有暫停、延遲切換、準(zhǔn)確估計任務(wù)進(jìn)度等功能,審計和在線控制功能可以讓它輕松地與監(jiān)控系統(tǒng)結(jié)合起來,必然非常受運維人員喜愛。 完美解決切換問題:表切換操作是在線修改表定義的最后一步,其它工具操作到這一步時常常會出現(xiàn)各種問題。Facebook OSC也曾詳細(xì)分析過這個問題,但它的最終方案是個非原子性切換:先把原始表改名,再把臨時表改名頂上??上г趦纱胃拿虚g會有一小段表不存在的時間,在這期間運行的業(yè)務(wù)語句都會失敗,因為目標(biāo)表不存在。Shlomi等經(jīng)過嚴(yán)密的論證和實驗,給出了原子性的兩階段切換方案:用一條連接去持有鎖,另一條連接做原子性的rename操作。在rename操作之前,會創(chuàng)建一張信號表,用它來阻塞rename操作,直到所有要求的表切換前提條件就緒。根據(jù)這個方案,表切換或者成功,皆大歡喜;或者失敗,則對業(yè)務(wù)無影響,也不會丟失數(shù)據(jù),還會釋放鎖讓業(yè)務(wù)繼續(xù),DBA只需要再一次用gh-ost重新嘗試切換即可。gh-ost按照MIT許可協(xié)議向開源社區(qū)發(fā)布。盡管現(xiàn)在已經(jīng)穩(wěn)定了,GitHub仍在計劃繼續(xù)改進(jìn)它,也非常歡迎來自開源社區(qū)的力量來一起試用和提出改進(jìn)意見。gh-ost是用GO語言實現(xiàn)的。
感謝杜小芳對本文的審校。