傳統(tǒng)DBA將死?餓了么數(shù)據(jù)庫(kù)自動(dòng)化運(yùn)維實(shí)踐
從時(shí)間軸上看,我們的 DBA 運(yùn)維平臺(tái)每年會(huì)有一個(gè)比較大的進(jìn)步,從人肉→工具化→平臺(tái)化→自助化,我們只用了兩年半時(shí)間完成全部迭代。
其中平臺(tái)化&自助化+數(shù)據(jù)庫(kù)多活改造,我們用了 8 個(gè)月的時(shí)間一口氣完成全部開發(fā)及改造工作。
在完成平臺(tái)化改造的同時(shí),我們數(shù)據(jù)庫(kù)架構(gòu)也從傳統(tǒng)的主從架構(gòu)發(fā)展到異地多活架構(gòu),這對(duì) DBA 的挑戰(zhàn)是巨大的,但這也是平臺(tái)必須能夠解決的。
因?yàn)閭鹘y(tǒng)的數(shù)據(jù)庫(kù)管理方式在當(dāng)前這種架構(gòu)下依靠 DBA 手工或者借助簡(jiǎn)單的工具是無(wú)法應(yīng)對(duì)多活架構(gòu) + 大規(guī)模管理帶來(lái)的復(fù)雜性,因此平臺(tái)化顯得非常重要。
隨著平臺(tái)化的推進(jìn),DBA 職能角色也在發(fā)生變化,過(guò)去 DBA 在運(yùn)維和維護(hù)上消耗,現(xiàn)在 DBA 更加專注業(yè)務(wù)做價(jià)值輸出。
我覺(jué)得 DBA 長(zhǎng)期在運(yùn)維層面過(guò)多花費(fèi)時(shí)間不斷修補(bǔ)各種層面漏缺,其實(shí)是不健康的,雖然每天很忙但是新問(wèn)題依舊會(huì)很多。
餓了么 DBA 運(yùn)維平臺(tái)概覽
我們的數(shù)據(jù)庫(kù)平臺(tái)主體功能概覽如下:
- DB-Agent:數(shù)據(jù)采集 + 進(jìn)程管理 + 遠(yuǎn)程腳本 & Linux 命令調(diào)用 + 與平臺(tái)耦合的接口。
- MM-OST:無(wú)傷 DDL 系統(tǒng)根據(jù) GH-OST 源碼改造實(shí)現(xiàn)多活場(chǎng)景下的數(shù)據(jù)庫(kù)發(fā)布。
- Tinker:Go 重寫了 Linux Crontab 的邏輯支持到秒級(jí) + 管理接口與平臺(tái)整合實(shí)現(xiàn)調(diào)度集群管理日常任務(wù)調(diào)度。
- Checksum:多機(jī)房數(shù)據(jù)一致性檢查。
- SqlReview:Go 實(shí)現(xiàn)的類似開源的 Inception SQL 審核工具并做了功能上的增強(qiáng)。
- Luna:優(yōu)化后的報(bào)警系統(tǒng)(大規(guī)模實(shí)例下如何減少報(bào)警且不漏關(guān)鍵報(bào)警)。
- VDBA:報(bào)警自動(dòng)處理系統(tǒng),代替 DBA 完成對(duì)線上 DB 的冒煙&報(bào)警的處理。
我不擅長(zhǎng)講一些方法論,下面給大家介紹具體點(diǎn)的內(nèi)容,讓大家有個(gè)更清楚的了解。
實(shí)時(shí)監(jiān)控&快速排障
這對(duì)于 DBA 是非常常見的事情,一般出問(wèn)題或者接到報(bào)警,通常都要登錄到服務(wù)器,一通命令敲下來(lái)可能花的時(shí)間最少兩分鐘,然后得出一個(gè)有慢 SQL 或者其他的什么原因。
這個(gè)診斷過(guò)程完全可以被自動(dòng)化掉,日常處理問(wèn)題的核心原則是“快”(我們高峰期線上故障一分鐘損失幾萬(wàn)單)。
而平臺(tái)必須能提供這樣的能力,出問(wèn)題時(shí)盡量減少 DBA 思考的時(shí)間直接給出現(xiàn)象 + 原因縮短決策時(shí)間(甚至必要時(shí)系統(tǒng)可以自動(dòng)處理掉有些問(wèn)題都不必 DBA 參與)。
基于我們的監(jiān)控大盤,DBA 可以清晰的知道當(dāng)前全服所有實(shí)例是否有異常,哪些有異常及是什么類型的異常或冒煙都會(huì)清晰呈現(xiàn)。監(jiān)控大盤將 DBA 日常管理過(guò)程中所有的命令集都整合到一起。
DBA 只需要簡(jiǎn)單的點(diǎn)點(diǎn)按鈕,系統(tǒng)就會(huì)自動(dòng)執(zhí)行所有命令并做好 SQL 執(zhí)行計(jì)劃分析、鎖分析、SQL 執(zhí)行時(shí)間分布、歷史趨勢(shì)分析,數(shù)據(jù)庫(kù)歷史 Processlist 快照查看等常見操作。
雖然這些功能看似簡(jiǎn)單,但是卻非常實(shí)用,提高了 DBA 故障定位的效率。
報(bào)警處理自動(dòng)化
報(bào)警處理自動(dòng)化目前主要包括:
- 空間問(wèn)題自動(dòng)處理
- 未提交事物處理
- 長(zhǎng)查詢自動(dòng) Kill
- CPU / 連接數(shù)據(jù) / thread runing 過(guò)多分析及處理
- 復(fù)制無(wú)損修復(fù)(1032,1062)
過(guò)去在處理復(fù)制數(shù)據(jù)不一致異常時(shí)同行都是 Skip 掉,但是這樣缺陷很多同時(shí)會(huì)留下數(shù)據(jù)不一致的隱患。
目前我們采用的是解析 Binlog 的方式來(lái)做到精確修復(fù),避免了傳統(tǒng)的 Skip 方式的缺陷。
有人可能會(huì)說(shuō)目前社區(qū)有很多開源的工具能解決你前面提到的報(bào)警問(wèn)題,為啥非要自己寫一個(gè)呢?
比如 PT 工具能幫你處理,Auto Kill 一些數(shù)據(jù)庫(kù)有問(wèn)題的 SQL,也能幫你跳過(guò)復(fù)制的錯(cuò)誤,或者 Github 上也有開源的實(shí)現(xiàn)能做到無(wú)損修復(fù)復(fù)制問(wèn)題?
這里的關(guān)于重復(fù)造輪的問(wèn)題,我覺(jué)得是對(duì)待開源態(tài)度的問(wèn)題,開源固然能解決一些問(wèn)題,但是不同的場(chǎng)景對(duì)應(yīng)不同的開源工具。 當(dāng)你把這些輪子拼湊到一起時(shí)難以形成一個(gè)有機(jī)整體。
尤其是你在進(jìn)行平臺(tái)化建設(shè)時(shí)必須要考慮清楚這個(gè)問(wèn)題,否則純粹的開源堆砌出來(lái)的系統(tǒng)是難以維護(hù)的不可靠的,對(duì)于開源我們可以用其思想造自己合適的輪子。
MHA 自動(dòng)化管理
在 8.0 之前絕大部分公司高可用實(shí)現(xiàn)還是基于 MHA。MHA 的實(shí)現(xiàn)不可避免要解決部署的問(wèn)題。
最初我們是做一個(gè)部署腳本在跳板機(jī)上,MySQL 安裝時(shí)就打通與跳板機(jī)的互信工作,然后由該腳本在來(lái)打通集群節(jié)點(diǎn)間的互信工作,然后在一個(gè) Slave 上啟動(dòng) MHA 管理進(jìn)程。
或者是將該管理進(jìn)程固定在集群外面的某一個(gè)或者多個(gè)服務(wù)器上集中部署&監(jiān)控,然而這樣會(huì)有什么問(wèn)題呢?
這樣會(huì)有如下幾點(diǎn)問(wèn)題:
- 重度依賴 SSH。
- 搭建過(guò)程復(fù)雜。
- Manager 管理節(jié)點(diǎn)外溢到一臺(tái)或多臺(tái)機(jī)器后影響可靠性的因素增多。
- 維護(hù)復(fù)雜,配置有效性存疑會(huì)因此造成穩(wěn)定性風(fēng)險(xiǎn)。
- 與平臺(tái)整合過(guò)于復(fù)雜,平臺(tái)如果要管理監(jiān)控 Manager 節(jié)點(diǎn)需要借助 SSH 或單獨(dú)實(shí)現(xiàn)一個(gè) Agent。
這種架構(gòu)管理幾十套或者上百套集群時(shí)還能勉強(qiáng)應(yīng)付。當(dāng)上千套時(shí),管理就很復(fù)雜,整體很脆弱,出問(wèn)題后維護(hù)工作量大。
我們?cè)谧?MHA 的方案時(shí),做了充分調(diào)查及論證,最終沒(méi)有選擇這種方式。
最終我們決定單獨(dú)搞一套管理方式出來(lái),大致邏輯是依托 Agent 來(lái)做到,基本原理如下。
- 每個(gè) DB-Agent 上都獨(dú)立實(shí)現(xiàn)如下接口:
- 獲取集群拓?fù)浣Y(jié)構(gòu)&:(self *MHA)GetDBTopology()
- 生成配置文件:(self *MHA)BuildMHAConfig()
- 節(jié)點(diǎn)互信:(self *MHA)WriteRsaPubilcKey()
- 啟動(dòng):(self *MHA)StartMHA()
- MHA 進(jìn)程實(shí)時(shí)監(jiān)控:(self *MHA)MHAProcessMonitor()
- 定時(shí)配置文件與拓?fù)浣Y(jié)構(gòu)匹配巡查:(self *MHA)InspectMHAConfigIsOK()
- 關(guān)閉:(self *MHA)StopMHA()
- 切換:(self *MHA)SwitchMHA()
平臺(tái)按照一定順序依次調(diào)用上述接口來(lái)完成整個(gè) MHA 的從搭建到管理的全部過(guò)程。
整個(gè)過(guò)程完全由平臺(tái)來(lái)完成,極大的減少了 DBA 維護(hù) MHA 的成本。過(guò)去 DBA 要配置或者 MHA 切換后的維護(hù)時(shí)間在 2-10 分鐘左右,現(xiàn)在控制在 3 秒以內(nèi)。
基于 Agent 的管理更加輕量級(jí),也避免了 Manager 節(jié)點(diǎn)外溢帶來(lái)的各種問(wèn)題,也避免了傳統(tǒng)的部署方式上的復(fù)雜性,維護(hù)零成本,與平臺(tái)整合非常簡(jiǎn)單。
平臺(tái)在將上述接口調(diào)用封裝成獨(dú)立的 API 后可供其他自動(dòng)化平臺(tái)調(diào)用,這將為下一步的完全無(wú)人管理提供支持。
資源池&一鍵安裝
過(guò)去業(yè)務(wù)擴(kuò)容需要 100 臺(tái)機(jī)器,提交給 Base 需求兩天后給你一個(gè) EXCEL 或者一個(gè) Wiki 頁(yè)面。
我們拿到機(jī)器之后去寫一些腳本,通過(guò)一些工具或者自動(dòng)化平臺(tái)刷資源環(huán)境檢查和安裝腳本,但是每個(gè)人可能做法不一樣,做出來(lái)東西五花八門,非常不統(tǒng)一。
別人維護(hù)的時(shí)候覺(jué)得沒(méi)有問(wèn)題,當(dāng)換你維護(hù)時(shí)候覺(jué)得很奇怪,為什么這樣做?不夠整齊劃一,標(biāo)準(zhǔn)化推行不是太好。
現(xiàn)在我們的 DBA 基本上不需要關(guān)心這些,DBA 只需要看我們資源池是否有空閑機(jī)器,如果資源不足只要負(fù)責(zé)申請(qǐng)資源即可,其他工作基本都可以由 Agent 自動(dòng)完成。
一鍵擴(kuò)容與遷移
我們 2015 年到 2016 年先后經(jīng)歷了遷移到 CDB,然后又遷移到 RDS,***又做自己的數(shù)據(jù)庫(kù)災(zāi)備系統(tǒng)。
這期間遷移的集群數(shù)超過(guò) 3000+ 套,平均每套集群遷移兩到三次,這么多遷移量,通過(guò)人工很難完成的。
以災(zāi)備為例,做災(zāi)備時(shí)候公司給我們 DBA 的團(tuán)隊(duì)遷移時(shí)間是兩周之內(nèi),那時(shí)候?qū)⒔?300 多套集群全部遷移到災(zāi)備機(jī)房里面去,實(shí)際上我們只用了兩天時(shí)間。
當(dāng)時(shí)我們一個(gè)人用了不到一個(gè)小時(shí)的時(shí)間寫了一個(gè)從集群搭建到調(diào)用數(shù)據(jù)庫(kù)遷移接口的腳本快速的拉起全部遷移任務(wù)。
自動(dòng)遷移會(huì)依托我們調(diào)度集群來(lái)完成全部的遷移工作。對(duì)于日常的自動(dòng)擴(kuò)容遷移,DBA 只需要一鍵即可完成全部遷移過(guò)程。
這里我們思考一下有什么手段可以完全避免 DBA 來(lái)點(diǎn)這一下按鈕呢?這里我覺(jué)得對(duì)于平臺(tái)化的過(guò)程其實(shí)也是所有操作 API 化的一個(gè)過(guò)程。
對(duì)于這點(diǎn),按鈕的動(dòng)作本身就是調(diào)用一個(gè) API,假設(shè)我們現(xiàn)在有一套更加高度自動(dòng)化的系統(tǒng)(有的大公司稱為智能系統(tǒng)^_^)能自動(dòng)判斷出容量不足時(shí)自動(dòng)調(diào)用該 API 不就完全自動(dòng)擴(kuò)容了嗎?
DBA 都不需要去人工觸發(fā),雖然這是小小的一個(gè)操作也能被省略(那么 DBA 后面該何去何從呢?)。
我們現(xiàn)在可以說(shuō)依靠平臺(tái)基本上完成了絕大部分標(biāo)準(zhǔn)化、規(guī)范化的工作,任何一個(gè) DBA 只要通過(guò)平臺(tái)來(lái)完成日常必要的工作,做出來(lái)的東西都是整齊劃一的,完全避免人的因素導(dǎo)致的差異。
誤操作閃回
2018 年至今,我們已經(jīng)做了差不多 4 次線上失誤操作,我們都在很短時(shí)間內(nèi)幫用戶做到快速回滾。
目前社區(qū)有很多關(guān)于回滾的解決方案,但是充分調(diào)研之后我還是決定自己造輪子(這里又回到前面提到的關(guān)于開源及造輪子的問(wèn)題)。
這里簡(jiǎn)單闡述原因:開源的優(yōu)點(diǎn)是通用性、普適性比較強(qiáng),但是場(chǎng)景化的定制一般比較麻煩。
目前的開源工具都是基于命令行來(lái)完成必要的操作,當(dāng)真的線上需要緊急回滾時(shí)還要登錄到服務(wù)器然后再輸入一堆的參數(shù)解析,這不符合我對(duì)平臺(tái)化的要求。
既然是平臺(tái)化,這一系列的操作起碼必須是能在界面里面選一選、點(diǎn)一點(diǎn)就能完成的。
也就是說(shuō)使用要足夠簡(jiǎn)單,尤其這類緊急操作花費(fèi)的時(shí)間要足夠短,沒(méi)必要當(dāng)著一堆開發(fā)的面把命令行敲的賊溜來(lái)秀肌肉。
我們的場(chǎng)景復(fù)雜舉例說(shuō)明:我們有一套集群?jiǎn)伪矸制?1024 片,總共分了 32 套集群,有一天開發(fā)突然找來(lái)說(shuō)有部分?jǐn)?shù)據(jù)被誤操作了,你該如何進(jìn)行處理?
這里表是被 Sharding 的,開發(fā)可能是不知道這批數(shù)據(jù)落在哪個(gè) Sharding 片里面。
所以你必須解析全部的 32 個(gè)節(jié)點(diǎn)上的 Binlog, 這時(shí)你通過(guò)開源的腳本吭哧吭哧起了 32 個(gè)進(jìn)程然后你的 CPU 爆了,網(wǎng)卡爆了……這里分片解析實(shí)際上在 32 個(gè)進(jìn)程是不行的。
如果解析腳本不支持對(duì)解析的 rowsEvent.Table 表名的正則匹配的話,恐怕要起 1024 個(gè)進(jìn)程……
考慮到上述場(chǎng)景有合適自己場(chǎng)景的解析工具是非常必要的。這里我用 Go 來(lái)實(shí)現(xiàn)采用了 github.com/siddontang/go-mysql/replication 解析模塊。
實(shí)現(xiàn)后的解析工具是一個(gè)服務(wù)化的組件,可以多節(jié)點(diǎn)部署應(yīng)對(duì)上述 Sharding 的解析場(chǎng)景,被服務(wù)化后可以被平臺(tái)直接調(diào)用。
當(dāng)真的出現(xiàn)失誤操作時(shí),DBA 操作時(shí)也不用揪心手抖……所以造每個(gè)輪子都有它的理由而不僅僅是愛(ài)好。
任務(wù)調(diào)度&歸檔
我們的調(diào)度服務(wù)其實(shí)是用 Go 重寫了 Linux Crontab 的邏輯并且支持到了秒級(jí)。
同時(shí)也為了方便管理加了一些管理模塊實(shí)現(xiàn)服務(wù)化,主要還是方便平臺(tái)調(diào)用(也是避免 DBA 手工去配置 Crontab )。
平臺(tái)對(duì)調(diào)度節(jié)點(diǎn)進(jìn)行整合實(shí)現(xiàn)一個(gè)邏輯上的調(diào)度集群(后續(xù)會(huì)改造成真正意義上的調(diào)度集群。其實(shí)改造方式也很簡(jiǎn)單,只要在調(diào)度節(jié)點(diǎn)里面加上節(jié)點(diǎn)自動(dòng)注冊(cè)然后加一個(gè)簡(jiǎn)單的任務(wù)分發(fā)器實(shí)現(xiàn)負(fù)載均衡即可)。
同時(shí)對(duì)日志功能做了增強(qiáng),通過(guò)調(diào)度可以自動(dòng)的把執(zhí)行過(guò)程中輸出的日志記錄下來(lái),方便日后追溯原因。
也支持捕獲并記錄調(diào)度腳本 Exit Code,方便對(duì)于有些特殊腳本并非只有成功 or 失敗兩種狀態(tài)的記錄。
舉個(gè)例子:比如一個(gè)腳本執(zhí)行過(guò)程中可能會(huì)有很多種 Panic 的可能,但是如果把這些 Panic 的原因都?xì)w結(jié)為腳本執(zhí)行異常并 exit(-1) [系統(tǒng)默認(rèn)的退出碼],這樣似乎也是可以的。
但是這樣 DBA 在檢查自己的任務(wù)狀態(tài)時(shí),發(fā)現(xiàn)異常時(shí)不能直接的定位錯(cuò)誤而是要去翻具體的執(zhí)行錯(cuò)誤日志,顯得不夠快捷(這也是用戶體驗(yàn)的點(diǎn))。
因此 DBA 只需要在平臺(tái)里面定義好錯(cuò)誤代碼對(duì)照表即可在 Painc 時(shí)捕獲異常,然后 Exit(exit_code) 就可以,當(dāng) DBA 巡查自己的任務(wù)時(shí)能清晰的知道錯(cuò)誤原因。
SQLReview
最初我們的 SQL 審核由開源的 Inception 實(shí)現(xiàn),但是由于我們需要加入更多校驗(yàn)規(guī)則,所以需要做一些定制修改。
然而團(tuán)隊(duì)內(nèi)隊(duì)友不太了解 C,因此很多情況下開發(fā)提交發(fā)布 SQL 工單后,都是由我們的 DBA 再來(lái)人肉審核一遍的。
我們現(xiàn)在平均每天 100+ 的 DDL,DBA 根本審核不過(guò)來(lái)。即使審核到了,還是會(huì)漏很多規(guī)則,人工是不能保證一定可靠的。
所以做自己的審核系統(tǒng)是很必要的,但是要獨(dú)立寫一個(gè) sql-parser 模塊難度還是非常大的。
在充分調(diào)研了 Python&Go 的開源實(shí)現(xiàn)后最終選擇了 TiDB 的解析模塊,于是項(xiàng)目很快就落地了。
在完全覆蓋 Inception 的規(guī)則后也做了相關(guān)擴(kuò)展,也就是加入我們自定義的規(guī)則。
擴(kuò)展索引的相關(guān)校驗(yàn):
- 冗余索引的校驗(yàn)。(如 ix_a(a),ix_ab(a,b))
- 索引中枚舉類型的校驗(yàn)。
- 組合索引中不能包含主鍵或者唯一索引。
- 建表時(shí)必須包含自增 id 的主鍵。
- 重復(fù)索引的校驗(yàn)。(如啼笑皆非的ix_a(a),ix_aa(a)求開發(fā)的心理陰影面積?)
- 組合索引列不能超過(guò) 3 個(gè)。
- 組合索引列時(shí)間等可能涉及范圍查詢的列(類型)必須放在***一位。(如ix_created_time_userid(created_time,userid)這樣的索引意義大嗎?)
- 索引泛濫攔截。(恨不得每個(gè)字段都建立一個(gè)索引……)
- Varchar (N>128)攔截或者提示。(警惕!開發(fā)可能要寫 like 了……)
- 索引命名規(guī)范檢查。(開發(fā)取的索引名稱五花八門,甚至有用的劣質(zhì) orm 框架生成一個(gè) uuid 的索引名稱,當(dāng) DBA 在進(jìn)行執(zhí)行 explain 時(shí)看到這個(gè)很頭疼根本看不出到底使用了什么索引,往往還要多執(zhí)行一次 show……)
風(fēng)險(xiǎn)識(shí)別攔截或者放行:
- 刪索引會(huì)根據(jù)元數(shù)據(jù)來(lái)判斷是否表或者索引是否在使用。(這依托大量的元數(shù)據(jù)收集&分析,過(guò)去 DBA 看見刪除操作很頭疼要各種驗(yàn)證,最終在操作時(shí)還要集群內(nèi)灰度操作)
- 禁止刪列操作。
- Modify 操作損失進(jìn)度檢查。(如 text->varchar,varchar(100)->varchar(10) 等都是禁止的)
- Modify 操作丟失屬性檢查。(該問(wèn)題很隱蔽可能有天開發(fā)說(shuō) default 值丟失了,那多半是某次 DDL 時(shí) Modify 語(yǔ)句沒(méi)有帶上原字段屬性導(dǎo)致的,當(dāng)這引發(fā)故障時(shí)肯定有人會(huì)指責(zé) DBA 為啥你沒(méi)審核到?MMP......)
- 禁止跨庫(kù)操作。(防止開發(fā)通過(guò) create table Notmydb.table 來(lái)意外的給別人創(chuàng)建表)
- 禁止一切 Truncate,Drop 操作。
內(nèi)建規(guī)范檢查:
- 大字段使用規(guī)范約束。(比如一個(gè)表里面超過(guò)一定比例的 Varchar,包含 longtext 等大文本類型)
- DB,表,索引命名規(guī)范約束檢查。
- 多活必要的字段及屬性檢查。
歷史校驗(yàn)結(jié)果數(shù)據(jù)沉淀:
- 通過(guò)數(shù)據(jù)分析準(zhǔn)確的知道哪些產(chǎn)研或者開發(fā)在上述方面犯錯(cuò)最多。(DBA 跟開發(fā)的關(guān)系往往也是斗智斗勇的關(guān)系你懂得……)
這里不得不說(shuō)的是過(guò)去我們?yōu)榱朔乐归_發(fā)違反上述規(guī)則,除了人肉審核外還對(duì)開發(fā)去培訓(xùn)但是這往往都沒(méi)有用,該犯的錯(cuò)誤還是會(huì)不斷的犯。
所以我們現(xiàn)在基本不在去搞什么培訓(xùn)了,完全由系統(tǒng)自動(dòng)來(lái)完成審核。
這里我一直強(qiáng)調(diào)的是任何標(biāo)準(zhǔn)化/規(guī)范化都是必須能夠?qū)戇M(jìn)代碼里的,否則實(shí)施起來(lái)必然有缺漏。
多活下的發(fā)布系統(tǒng)
數(shù)據(jù)庫(kù)多活的架構(gòu)大致是這樣:M?DRC?M。這里 DRC 是我們的多機(jī)房數(shù)同步工具,這里可以把數(shù)據(jù)庫(kù)多活理解成雙 Master 系統(tǒng),只是用 D 代替了雙 Master 下的原生復(fù)制。
這種架構(gòu)下對(duì) DBA 的維護(hù)挑戰(zhàn)還是非常大的,時(shí)間關(guān)系只分享關(guān)于數(shù)據(jù)庫(kù)發(fā)布的相關(guān)內(nèi)容,這也是最重要的一塊。
說(shuō)到數(shù)據(jù)庫(kù)發(fā)布基本上就是在說(shuō) DDL 對(duì)吧,一直以來(lái) DDL 對(duì)開發(fā)來(lái)說(shuō)都是非常頭疼的,DBA 往往會(huì)選擇 PT 工具來(lái)完成 DDL 操作。
但是受到 PT 是基于觸發(fā)器實(shí)現(xiàn)的,影響 DDL 期間會(huì)產(chǎn)生鎖等待現(xiàn)象,這會(huì)造成業(yè)務(wù)上的影響,過(guò)去我們也在這上面吃過(guò)很多次虧。
Alter 通過(guò)什么方式來(lái)進(jìn)行?有如下幾個(gè)方式:
原生 DDL 多機(jī)房并行執(zhí)行:DRC 不支持,機(jī)房間延遲不可控,機(jī)房?jī)?nèi)延遲巨大。
PT-OSC 多機(jī)房并行執(zhí)行:Row 模式下大表的 DDL 會(huì)產(chǎn)生大量的 Binlog,IDC 間的網(wǎng)絡(luò)瓶頸造成全局性影響。
PT 工具在不同機(jī)房間最終的 Rename 階段時(shí)間點(diǎn)不同,造成機(jī)房間數(shù)據(jù)結(jié)構(gòu)不一致導(dǎo)致 DRC 復(fù)制失敗,最終導(dǎo)致不可控的數(shù)據(jù)延遲。
基于觸發(fā)器的實(shí)現(xiàn)會(huì)產(chǎn)生額外的鎖,對(duì)業(yè)務(wù)影響明顯;基于 PT 源碼改造困難也難以與平臺(tái)整合(3P 語(yǔ)言只剩下了 Python……)。
Gh-OST 多機(jī)房并行執(zhí)行(基于 Go 實(shí)現(xiàn)):增量數(shù)據(jù)基于 Binlog 解析實(shí)現(xiàn)避免觸發(fā)器的影響;基于 Go 實(shí)現(xiàn)為改造提供可能。
關(guān)于 GH-OST 我不打算多講,大家可以去 Github 上看作者對(duì)實(shí)現(xiàn)原理的說(shuō)明,這里還是簡(jiǎn)要提一下大致工作流程:
- 創(chuàng)建中間表臨時(shí)表。
- 對(duì)該臨時(shí)表進(jìn)行 DDL。
- 在 Master 或者 Slave 上注冊(cè),Slave 接收 Binlog 并解析對(duì)應(yīng)表的 Events 事件。
- Apply Events 到臨時(shí)表。
- 從原表 Copy 數(shù)據(jù)到臨時(shí)表。
- Cut-Over(我們的改造點(diǎn)從這里開始)相當(dāng)于 PT 的 Rename 階段。
我們做了一個(gè)協(xié)調(diào)器,每個(gè) GH-OST 在 DDL 過(guò)程中都上報(bào)自己的執(zhí)行進(jìn)度,同時(shí)我們?cè)?Cut-Over 前加了一層攔截。
必須等待多個(gè) GH-OST 都完成數(shù)據(jù) Copy 后,多個(gè) GH-OST 節(jié)點(diǎn)才會(huì)同時(shí)進(jìn)入 Cut-Over 階段。
這樣就保證了多機(jī)房同步 Rename 進(jìn)而來(lái)避免延遲的產(chǎn)生(事實(shí)上我們機(jī)房間的延遲都控制在秒級(jí))。這里大家可能會(huì)有疑問(wèn)直接在一個(gè)機(jī)房做不行嗎?可以依靠 DRC 同步啊?
首先 DRC 不支持 DDL 操作,這樣就決定了沒(méi)法通過(guò) DRC 同步方式來(lái)進(jìn)行,其次機(jī)房間帶寬有限 DDL 期間產(chǎn)生大量 Binlog 會(huì)造成帶寬打滿的問(wèn)題。
我們?cè)谶M(jìn)行雙機(jī)房同步 DDL 時(shí),為了防止 DRC 應(yīng)用了 GH-OST 產(chǎn)生的 Events,DRC 會(huì)主動(dòng)丟棄 GH-OST 產(chǎn)生的 Binlog 具體是根據(jù) TableName
命名規(guī)則來(lái)區(qū)分。
對(duì) GH-OST 的改造還包含添加多機(jī)房負(fù)載均衡功能,由于 DB 是多機(jī)房部署的,你的 GH-OST 工具肯定不能部署在一個(gè)機(jī)房里(解析 Binlog 速度太慢,Copy 數(shù)據(jù)過(guò)程非常慢主要是消耗在網(wǎng)絡(luò)上的延時(shí)了)。
但是多機(jī)房部署也還是不夠的,還得是每個(gè)機(jī)房都部署幾套 GH-OST 系統(tǒng)。
因?yàn)楫?dāng)開發(fā)同時(shí) DDL 的量比較大時(shí),單臺(tái) GH-OST 系統(tǒng)會(huì)因?yàn)橐馕龅?Binlog 量非常大導(dǎo)致 CPU、網(wǎng)卡流量非常高,影響性能(跟前面提到的閃回功能是同一個(gè)道理)。
搞定發(fā)布系統(tǒng)后,DBA 再也不用苦逼的值班搞發(fā)布了,起初我們搞了一個(gè)自動(dòng)化執(zhí)行系統(tǒng),每天系統(tǒng)會(huì)自動(dòng)完成絕大多數(shù)的工單發(fā)布工作。后來(lái)我們完全交給開發(fā)來(lái)執(zhí)行。
現(xiàn)在從開發(fā)申請(qǐng)發(fā)布到最終發(fā)布,完全由開發(fā)自助完成,自助率平均在 95% 左右,極少有 DBA 干預(yù)的情況。
隨著數(shù)據(jù)庫(kù)的不斷進(jìn)步與完善甚至開始往 SelfDrive 上發(fā)展加之這兩年 DevOps,AIOps 的快速發(fā)展。
也許留給傳統(tǒng)運(yùn)維 DBA 的時(shí)間真的不多了(不是我在鼓吹相信大家也能感受的到),我想除了時(shí)刻的危機(jī)感 + 積極擁抱變化外沒(méi)有其他捷徑了。
作者:蔡鵬
簡(jiǎn)介:2015 年加入餓了么,見證了餓了么業(yè)務(wù)&技術(shù)從 0 到 1 的發(fā)展過(guò)程,并全程參與了數(shù)據(jù)庫(kù)及 DBA 團(tuán)隊(duì)高速發(fā)展全過(guò)程。同時(shí)也完成個(gè)人職能的轉(zhuǎn)型,由運(yùn)維 DBA 到 DEV-DBA 的轉(zhuǎn)變,也從 DB 的維穩(wěn)轉(zhuǎn)變到專心為 DBA 團(tuán)隊(duì)及 DEV 團(tuán)隊(duì)的賦能。