披荊斬棘,餓了么數(shù)據(jù)庫高可用架構演進!
本文將和大家分享餓了么作為高速發(fā)展的互聯(lián)網(wǎng)企業(yè)之一,在發(fā)展歷程中數(shù)據(jù)庫技術如何跟隨企業(yè)發(fā)展并不斷滿足業(yè)務的需求。
分享內(nèi)容大致涉及到以下五點:
- 數(shù)據(jù)庫架構怎么滿足業(yè)務、支撐業(yè)務發(fā)展
- 怎么提高數(shù)據(jù)庫的可用性
- 如何對數(shù)據(jù)流進行相應的控制和保護
- 規(guī)模大了以后如何提高數(shù)據(jù)庫運維的效率
- 一些個人認為重要原則的總結
首先簡單介紹一下餓了么的概況,點過外賣的同學應該都知道餓了么吧?
餓了么發(fā)展最快階段也是最近四五年的事情,我是 2015 年進入餓了么的,那時每天才幾十萬的訂單,服務器也不多。
到了 2016 年時每天訂單達到了幾百萬,商戶也更多了;而 2017 年不管是訂單、運單都是千萬以上,直到現(xiàn)在都還在快速增長。
這么多數(shù)據(jù)的產(chǎn)生對底層數(shù)據(jù)存儲是非常大的挑戰(zhàn),而且那個時候要在非常短的時間內(nèi)應對業(yè)務的爆發(fā)性的增長,所以當時底層的技術挑戰(zhàn)也是非常大的。
數(shù)據(jù)庫架構
垂直拆分
在數(shù)據(jù)庫架構方面餓了么開始的時候也是比較原始的階段,最初是一主多從的架構;發(fā)展到后面發(fā)現(xiàn)訂單數(shù)據(jù)庫很難再滿足業(yè)務往上增長的需求了。
過百萬之后一主不管幾從都很難滿足業(yè)務的需求(因為寫太大),這就面臨著需要拆分的情況,需要把熱點業(yè)務單獨拆出來,把一套數(shù)據(jù)庫拆成多套,進行垂直的業(yè)務拆分。
數(shù)據(jù)庫架構-垂直
拆分根據(jù)什么原則呢?又怎么來預算訂單庫現(xiàn)在的架構能承載多少的 TPS 和 QPS 呢?
我們當時是結合訂單量、對應的 QPS、TPS 數(shù)據(jù),再根據(jù)半年后的增長情況來推算出每個業(yè)務大概會產(chǎn)生出多少的 QPS、TPS。
然后結合每套集群能承載的 TPS、QPS 數(shù)量(基于壓測),就能估算出需要拆分成什么樣的結構、以及拆分后每一套(半年后)大致需要承載多少 TPS、QPS。
當時按業(yè)務垂直拆分后,這一套方案承載了 200、300 萬訂單的規(guī)模,垂直拆分的優(yōu)勢是代價小見效快(業(yè)務代碼改動并不大),能快速有效的支撐業(yè)務。
水平拆分
雖然按垂直架構拆分完了,但是熱點的地方依然還是熱點,比如說訂單隨著下單量的增長依然會變成很大的瓶頸。
那如何打破瓶頸呢?我們需要把訂單這個熱點再單獨拆分成多套。
數(shù)據(jù)庫架構-水平
也就是行業(yè)里說得比較多的“水平拆分”,把原來的一張訂單表在底層拆成 1000 張小表,放在不同的集群上。
這樣即使這一個訂單的量再大,也能夠通過不斷水平擴容機器來將壓力拆分到更多的集群上,從而滿足了熱點的性能承載。
我們可以通過壓測計算出每套集群能承載出多少 QPS 和 TPS,再結合現(xiàn)在的業(yè)務情況就能估算出多少訂單會對應產(chǎn)生多少的 QPS 和 TPS。
進而也能知道拆分成多少套集群能承載多少的業(yè)務量,所以也就知道了要擴多少機器才能滿足半年或者一年后的業(yè)務增長。
如果說底層因為擴容做了分片策略,但是這個改動對業(yè)務不是透明的話,那就意味著業(yè)務需要做很多改造來適應底層的分片邏輯,一般業(yè)務是很難接受的。
所以,我們的水平拆分為了對業(yè)務做到透明,需要做一層代理層(我們叫 DAL),代理層會幫助業(yè)務代碼做到對數(shù)據(jù)庫底層拆分邏輯的訪問透明,業(yè)務看到的還是一張訂單表,但是底層變了 1000 張表。
完成水平擴容后基本上所謂的熱點也不會存在太大的瓶頸,如果再往上增長的話可以繼續(xù)拆小,繼續(xù)添加更多的機器來承載。
多活架構
垂直拆分之后,我們就沒有性能瓶頸了嗎?其實機房也會成為我們的瓶頸。一般來講企業(yè)都是租賃供應商的機房,一個機房能進多少機器不是沒有限制的,供應商也不可能給你預留太多的位置。
當你服務所在的機房放不進去機器以后,你會發(fā)現(xiàn)雖然技術架構能滿足水平擴容,但是在物理上不能再加機器了,性能瓶頸依然會到來。
為打破單個機房面臨的容量瓶頸,我們需要擴容到更多的機房,所以我們做了“多活架構”。
數(shù)據(jù)庫架構-多活
在每個機房數(shù)據(jù)庫都是簡單的主從架構,下單的時候北京用戶可以在頭個機房下單,上海用戶可以在第二個機房下單。
這樣的話下單的高峰壓力會分散到多個點去,同一套集群在兩個機房都有部署,意味著承載的性能變大了。
這個時候就可以在兩個機房放機器,如果一個機房的機器滿了放不下,我們可以把流量引到另一個機房,擴容另一邊機房,這樣就打破了單個機房對容量的限制。
我們多活邏輯是根據(jù)用戶所在的位置決定他在哪個機房下單,可能他今天在北京明天在上海,下的單在不同的機房。
要讓用戶看到所有的數(shù)據(jù),就必須要讓數(shù)據(jù)雙向流通起來,所以多機房之間做數(shù)據(jù)的相互流通是數(shù)據(jù)庫做多活的必備條件。
有很多企業(yè)做的是熱備,只是在一邊下單,但是另一邊是 backup 的狀態(tài),一旦這邊出現(xiàn)問題以后再切到那邊,這樣的架構并不能解決性能問題,而且資源利用率很低(有多少公司出問題真敢切?)。
而我們多活的架構可以在兩個機房同時下單,能讓性能、資源利用率和可靠性都得到明顯提高。
數(shù)據(jù)庫架構層面從垂直拆分、水平拆分到多活后,基本能滿足絕大多數(shù)企業(yè)的業(yè)務發(fā)展了,這當中我們有兩個組件發(fā)揮了重要的作用,也一起介紹下。
①DAL
代理層(DAL),最直觀的需求是能做分庫分表,能做讀寫分離,還可以做資源隔離、連接數(shù)隔離、連接的管理等,更重要的是還能對數(shù)據(jù)庫進行相應的保護。
外賣業(yè)務大多數(shù)人都是在中午下單,所以 11 點左右是餓了么的業(yè)務高峰。
為了緩解數(shù)據(jù)庫壓力,我們會通過 DAL 層做削峰處理,當流量過大時我們會讓用戶消息做排隊的處理,由此緩解對數(shù)據(jù)庫的瞬間沖擊。如果流量特別大的時候還可以做限流、熔斷等處理。
還有黑白名單機制,大家了解數(shù)據(jù)庫運維的話會知道,如果研發(fā)寫的 SQL 有問題,放入到數(shù)據(jù)庫里風險會比較高。
如果他現(xiàn)在發(fā)了一個刪除表的 SQL 命令過來就有風險,我們的 DAL 就會把這類黑名單 SQL 給拒絕。
更高級一點的功能是多維分表以及全局表 Map Table 功能。有些配置表希望在所有機房都有,DAL 上就可以做 Global Table 的功能,可以保證在所有節(jié)點上都是同樣的數(shù)據(jù)。
當然,DAL 做完這些功能后,對 SQL 也是有一些限制的。比方說事務,下單不能跨服務片去做事務。
很多傳統(tǒng)的應用業(yè)務邏輯會把很多東西包在一個事務里完成,但互聯(lián)網(wǎng)業(yè)務應該盡量減少這種應用,在底層分片后,業(yè)務事務并不能完全通過數(shù)據(jù)庫里的事務來保障。
可能每個表分片維度不一樣,會導致數(shù)據(jù)分布到不同的機器上,這樣就需要跨服務器事務一致性的保障,所以業(yè)務就不能再依賴于數(shù)據(jù)庫的事務,需要通過其他的機制來保證。
還有 Order by 、Group by 會受到限制,如果你查 Top10 的話,DAL 只會在一個分片上把 Top10 給到你,但并非是全局的。雖然有這些限制,但是與 DAL 帶來的好處比是完全可以接受的。
②DRC
數(shù)據(jù)同步組件 DRC,實現(xiàn)的功能是在一邊機房接受變更日志,并把變更日志傳遞到其他的機房去,其他機房再能把這個變更應用上。
為什么用 DRC 組件而不用 MySQL 原生復制呢?因為我們的鏈路是跨機房跨地域的,上海和北京遠距離的傳輸下,使用原生復制的話缺乏靈活性。
比方 MySQL 會產(chǎn)生各種各樣的消息,尤其是做維護操作時要加字段的話,會產(chǎn)生大量的變更日志,這時直接傳遞就會導致網(wǎng)絡直接堵死。
在北京和上海的帶寬就 5~10G 的情況下,一個 DDL 變更 100G 的表,會把帶寬打滿,這樣很容易造成大的故障。
而且用DRC還可以做很多事情:
- 比如說無用消息的過濾,MySQL 平時會產(chǎn)生很多通知消息,但我們只需要數(shù)據(jù)變更的消息,就只需要傳遞變更信息。
- 可以做數(shù)據(jù)包的壓縮,還可以做維護操作的過濾。維護操作可以在兩個機房同時進行,并且不希望用復制的方式來傳遞,這樣的話避免了在維護上產(chǎn)生大量的變更消息,導致網(wǎng)絡阻塞等問題。
- 再比如說數(shù)據(jù)發(fā)生沖突了該怎么處理?還有怎么避免數(shù)據(jù)的環(huán)路。如果變更日志 A 寫在上海機房,但是 A 變更傳遞到北京機房后,又會更新北京機房的日志,A 又會通過北京機房的變更重新傳回到上海機房,這樣就是環(huán)路了。
DRC 會對相應的變更來源打上標簽,這樣數(shù)據(jù)就可以控制不回到自己產(chǎn)生的機房里。
數(shù)據(jù)庫的高可用
下一步是怎么提高數(shù)據(jù)庫的可用性。整個網(wǎng)站的可用性是由多部分完成的,數(shù)據(jù)庫只是其中的一塊,所以數(shù)據(jù)庫可用性要做到比整體可用性更高。
比如做三個九的網(wǎng)站可用性,那底層需要四個九,甚至五個九的可用性來保證。
架構
我們都知道物理上的故障是不可避免的,任何一臺機器都有可能出現(xiàn)故障,任何一個設備都有可能故障,所以我們需要針對可能出現(xiàn)故障的地方都有相應的高可用方案。
EMHA
一臺 Master 機器出故障的時候,我們的 HA 是基于開源 MHA 改造的 EMHA。
在每個機房里 EMHA 管理每個機房 Master 出現(xiàn)故障時的切換,也不光是只負責出故障的切換,還要求控制切換時間在 30s 左右,同時要把故障拋給其他需要通知到的地方。
比如代理就要知道這臺 Master 已經(jīng)掛了后新的 Master 是誰,所以 EMHA 切換時需要把消息擴散出去,讓所有需要信息的組件、環(huán)節(jié)都能接受到信息。
這樣就能達到主庫掛的時候?qū)I(yè)務的影響非常小(可能業(yè)務都沒有感知),DB 切換同時自動完成切組件的對接,由此來提高可用性。
多分片方案
如果對下單業(yè)務沒有辦法做到機器不出故障,但希望出故障時影響非常小,可以做分片方案。
比如說分成了 10 個片放 10 臺機器上,這個時候一臺機器出故障影響的是 1 個片,整體只會影響十分之一的業(yè)務。
如果你把片分得足夠小的話影響的范圍會變得更小,我們對關鍵的業(yè)務會進行更細的分片,一個片壞了也只能影響 1/n 的業(yè)務。
異地多活
異地多活后一個機房出問題不會受到多大影響,因為機房間切換的時間就在幾分鐘內(nèi)能完成,這就能讓系統(tǒng) Online 的時間大大提高。
另外,做重要維護的時候可以把一個機房的流量全切走,在沒有流量的機房做相應的維護動作,維護完成之后再把流量切過來,然后操作另外的機房,這樣風險特別高的維護操作也不用做關站處理。
一般大型一點的網(wǎng)站做一次關站維護需要的時間很長;以上這些點是從架構上能把可用性一層一層往上提。
下面我們再看下從故障發(fā)現(xiàn)和處理的角度怎么提高可用性。
故障
可用性還有很重要的點——既然故障不可避免,那我們就要追求如何快速地發(fā)現(xiàn)問題,解決問題。
Trace
全鏈路跟蹤從應用(appid)一直下串到 DB,包括有接入層、應用層、中間層、服務層、代理層、緩存層、數(shù)據(jù)庫層等串聯(lián)起來。
TraceID 能提供正反向異?;ネ颇芰Γ篒D 會從上往下串,不管你在哪一層發(fā)現(xiàn)的問題,拿到 ID 就可以查看鏈路上哪些環(huán)節(jié)有問題(哪個環(huán)節(jié)耗時最長或者出異常),這樣就可以及時地定位問題。
如果每個地方各查各的話,時間消耗是很長的,有 Trace 系統(tǒng)后,定位問題的效率會提高很多。
還有在數(shù)據(jù)庫層面來看 80%~90% 的問題都是 SQL 問題,如果能及時獲取有問題 SQL,判斷這個 SQL 的來源,并對某些非關鍵的問題 SQL 進行限流或者攔截訪問的話,就能隔離問題 SQL 的影響,減少 DB 故障。
VDBA
我們在數(shù)據(jù)庫層開發(fā)了一個 VDBA 的自動處理程序,它會不停地對所有的數(shù)據(jù)庫進行掃描,根據(jù)我們制定的規(guī)則判斷狀態(tài),如果發(fā)現(xiàn)有問題的 SlowSQL 會根據(jù)引起異常的程度進行限流、查殺、拒絕等操作。
當然 VDBA 能處理的不僅僅是 SlowSQL,還有系統(tǒng)出現(xiàn)堵塞了,有未提交的事務、復制中斷、Blocked、binlog 太大了需要清理等都能處理。
很多事情讓 VBDA 自動處理后,不僅效率提高了,也大大減少人操作的風險。
在故障處理時加快故障的定位時間和故障自動處理的機制后,可用性會得到明顯的提升。
數(shù)據(jù)流控制
數(shù)據(jù)流控制也依賴于剛剛所說的一些組建。作為數(shù)據(jù)的管理人員,理論上應該有自己的手段來控制什么樣的數(shù)據(jù)能進入,什么樣的 SQL 能通過,要以什么樣的方式來存儲等。
把控不是說你寫寫文檔就能把控住的,需要有相應強制的手段和工具。每個業(yè)務訪問數(shù)據(jù)庫能使用多少連接、帳號權限是什么樣的都需要有比較標準的控制,這樣能夠讓所有數(shù)據(jù)在進來的時候就能夠在 DBA 的掌控當中。
數(shù)據(jù)進來以后需要生產(chǎn)落地存儲,落地后的數(shù)據(jù)也需要再傳遞到其他地方,這些都需要有相應的控制。
比如說現(xiàn)在大數(shù)據(jù)要拿數(shù)據(jù),我們就可以通過 DRC 的消息來推送給大數(shù)據(jù),這樣就不需要再掃描數(shù)據(jù)庫來拿數(shù)據(jù)了。
原來的大數(shù)據(jù)通過 Sqoop 任務都是隔天隔小時拉取數(shù)據(jù),但現(xiàn)在可以做到實時的數(shù)據(jù)傳遞,做營銷活動時可以實時看到營銷的效果。
數(shù)據(jù)產(chǎn)生后還可能需要對外提供,如要把生產(chǎn)的數(shù)據(jù)同步到測試環(huán)境和開發(fā)環(huán)境;這個時候可以由 DataBus 來幫你同步數(shù)據(jù),生產(chǎn)數(shù)據(jù)外傳需要做數(shù)據(jù)脫敏和清洗操作(尤其是手機號、ID號)。
原來是比較麻煩的,現(xiàn)在研發(fā)只需要管同步的配置信息就可以了,組件會自動脫敏和清晰,非常方便,也符合安全的規(guī)范。
運維提效
重點講一下關于運維提效:在一個有上千號研發(fā)人員的公司,如果只有一堆規(guī)范文檔之類的來維護規(guī)則是很難把控的,因為人員有離職的、新進入的,不可能跟每個人都去宣傳,所以必須要有平臺來管控。
SQL 治理
首先在 SQL 發(fā)布的時候,我們平臺上的發(fā)布工具里面會內(nèi)嵌需要遵循的標準,如果表建的時候不符合標準是沒法生產(chǎn)提交的,這樣就強制地把規(guī)則和標準變成硬性要求,SQL 還可以自動實現(xiàn)審核也節(jié)省了 DBA 很多時間。
另外,生產(chǎn)一旦出現(xiàn)變慢的 SQL 后,監(jiān)控系統(tǒng)會馬上把消息 Push 給研發(fā),如果影響到生產(chǎn)運作的話會直接拒掉、查殺掉。
比方我們定義超過 30 秒的 SQL 是不允許線上產(chǎn)生的,這類 SQL 會被直接殺掉,這樣可以大大減少生產(chǎn)的風險。
自助發(fā)布
很多公司的 DBA 大部分時間在審核 SQL 和發(fā)布 SQL,而我們的 SQL 都是研發(fā)自助發(fā)布的,不需要 DBA 操心。
我們平臺支持原生、PT 執(zhí)行、mm-ost 執(zhí)行(餓了么自行改造的數(shù)據(jù)庫多機房同步發(fā)布工具),發(fā)布平臺會幫他們計算好你的發(fā)布大概需要多長時間,甚至會給你判斷什么時候是業(yè)務低峰(那個時候發(fā)布會比較好),這樣研發(fā)對自己的發(fā)布也是比較有把控力的。
自助歸檔
歸檔操作也是個比較頻繁的需求,一旦生產(chǎn)產(chǎn)生大量數(shù)據(jù)后,就需要做冷熱數(shù)據(jù)分離,要把不需要經(jīng)常用的數(shù)據(jù)搬走。
原來這個操作是比較費勁的,需要 DBA 跟在后面做很多事情,而現(xiàn)在只需要研發(fā)自助解決。
如果你的表超過 1000 萬就需要部署歸檔任務了,這個時候會推送消息給研發(fā)告訴他你的表已經(jīng)超過標準了,需要部署歸檔任務,研發(fā)自己就可以在平臺上把表的歸檔規(guī)則填上去,完成審批后后臺幫你自動地做這件事情了。
還有關于 DB 的備份和恢復,一旦數(shù)據(jù)庫部署到生產(chǎn)后,在后臺的系統(tǒng)里會幫你自動地部署備份、恢復任務和自動校驗可用性,你還可以在平臺上完成數(shù)據(jù)的回檔,一旦數(shù)據(jù)刷錯了、寫錯了通過平臺就能找回。
數(shù)據(jù)保障和遷移
對 DBA 來講需要把一個數(shù)據(jù)庫從這臺機器搬到另外的機器上,需要把一個大表拆分成多張小表,類似的動作就需要搬數(shù)據(jù)。
我們做了數(shù)據(jù)搬遷的工具,你只需要做配置就可以了,配置完成之后可以自動搬數(shù)據(jù)了,也會減少 DBA 很多工作量。
云實踐
現(xiàn)在餓了么所有的開發(fā)測試環(huán)境都是在云上的,效率比自己做環(huán)境高很多,隨時需要隨時拿,用完隨時釋放。
另外還可以做彈性,彈性伸縮比較難,現(xiàn)在我們也沒有完全實現(xiàn),但正在朝著這個方向努力。
我們業(yè)務的曲線是午高峰和晚高峰,這個時候流量很大,彈性調(diào)度需要在業(yè)務高峰的時候把機器加上,在業(yè)務低峰的時候把機器回收回去,提高機器的利用率。
云機房后面會承載我們主要的流量,云機房的好處是底層管理不需要自己負責,擴容資源比較方便,這樣能提高交付的效率。
在云上的機房可以灰度引流,剛開始可以很少的流量去做,當我們覺得它很穩(wěn)健之后就可以把流量逐步往上遷,這樣能逐步把云平臺的優(yōu)勢利用起來,把資源動態(tài)伸縮的環(huán)境利用起來,同時也能控制風險。
所以,現(xiàn)在利用云來提高運維效率是很好的手段。
建議原則
總結一下,從我們做這些事情里面抽取我個人感覺比較重要的點是什么呢?
①最小可用性原則
不管是對帳號的處理、連接的處理、SQL 的標準都應該有比較嚴格的限制,不能使用太多的資源,也不應該占用太多的資源。
最小可用性原則就是你的連接數(shù)平時只用 20 個,那我給你 40 個,有一倍波動的空間就可以了。還有帳號權限只需要增、改、查的權限,這樣就不會給你刪除的權限。
②Design for Failure
這是我們 CTO 經(jīng)常講的,設計環(huán)節(jié)不管是在運維規(guī)劃還是代碼的環(huán)節(jié)都應該考慮接受失敗的情況。
不管是物理層面還是架構層面的基礎設施一定會出現(xiàn)問題,這個時候優(yōu)良的架構必須要考慮應對錯誤情況,確保這類波動和短暫的問題能做到容錯和隔離,不至于導致整體的崩潰,同時具備快速恢復的能力。
③標準、流程、自動(助)、量化
一開始應該設定好標準,接著把標準拆解成流程,再把流程做成自動化、自助化的處理,進而達到維持整體標準的不變形,同時提高效率的目的,盡可能做到可量化。
比如去年我們維護 100 個 DB 實例需要兩個 DBA,今年效率提升后也許一個就可以了,量化反過來也能促進運維效率的提升(可以知道哪些環(huán)節(jié)最消耗人力和資源,針對性的優(yōu)化后效率就提高了)。
④灰度、限流、熔斷、隔離
變更是系統(tǒng)穩(wěn)定性的很大變數(shù),想要提高整體的可用性必須對變更環(huán)節(jié)有苛刻的限制要求。
比方我們要求所有的發(fā)布必須先灰度,灰度完成之后在發(fā)一邊的機房,然后再全量化,要有快速回退手段;然后程序要求有過載保護處理,具備限流、熔斷和隔離等兜底措施。
⑤穩(wěn)定、效率、成本
這三點應該是企業(yè)對技術部門的核心訴求,也是有追求的技術團隊不斷努力的方向。
⑥要方向,更要落地
今天介紹的內(nèi)容經(jīng)歷過的人都知道每一步都不容易,對于基礎設施還很薄弱的公司來講,最重要的還是考慮自己能夠用得上的,先要有落腳點,哪怕從最基礎的問題開始,把問題一項一項解決。
然后再逐步完善,一步步的改變才能真正讓用戶、公司感覺到團隊的價值。所以講了這么多,最重要的還是要落地。
虢國飛,餓了么數(shù)據(jù)技術部負責人。從事數(shù)據(jù)庫行業(yè)十余年,專注于 MySQL、PGSQL、MSSQL 等數(shù)據(jù)庫領域的管理、研究和平臺的研發(fā)等工作。目前在餓了么主要負責數(shù)據(jù)庫和相關中間件的管理、開發(fā)和維護工作。