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