網易分佈式數據庫多活架構的演進與實踐

本文由 dbaplus 社羣授權轉載。

大家好,今天給大家分享一些網易近幾年在數據庫多活方向上的工作。

我將簡單介紹下爲什麼我們要做數據庫多活,再從三個階段介紹網易在數據庫多活上做的工作。

一、數據庫多活的目標

數據庫多活的目標包括“容災”和“提升處理能力”兩方面。

容災可以簡單理解爲當系統由於外部或內部原因出現部分不可用時,仍然能在短時間內恢復可用。而容災最常用的手段即是備份,在數據庫領域不僅需要對計算能力做備份也要對數據做備份。容災級別可以劃分爲宿主機級別的容災、機房級別的容災和跨城的容災。

要提升數據庫的處理能力,一般有讀寫分離和單元化兩種方式,讀寫分離將數據庫的備份節點的讀能力提供出來,其實提升了系統的利用率,而單元化則是當整個機房的資源無法容納業務的增長時,加入更多的機房資源,擴展業務的處理能力的方法。

二、網易的產品特性

網易成立23年以來,孵化了大量的產品,不同的產品特性、產品發展到不同階段對數據庫多活都有不一樣的需求。

像音樂、新聞這類的泛娛樂產品,它們每日要處理大量的請求,同時也會產生大量的數據,而由於其產品的獨特性,它們對數據的一致性要求往往不是特別高,但對可用性要求比較高。

而嚴選這樣的電商類應用,由於其獨有的交易類數據,對數據一致性要求是比較高的,流量一般小於娛樂性應用,對可用性同樣很高。

而最爲極端的支付類應用,由於所有的業務都和錢掛鉤,對數據一致性要求極高,流量反而最小,可用性要求同樣很高。

對一致性要求的不同會導致不同的產品在選擇數據庫多活方案時有不一樣的選擇,而互聯網產品對可用性的要求往往是一致的,都無法接受產品長時間不可用。

三、網易數據庫使用現狀

我從DBA同事那邊要到了一份網易當前的數據庫選型分佈數據。

由於其穩定性和開源的特性,超過一半的數據庫實例是MySQL。而緩存則是Redis使用最廣,數據量較大時會選用HBase。Oracle仍然在一些老的系統內有使用,而其他一些NewSQL數據庫則還在測試驗證階段。

單實例的MySQL處理能力有限,無法滿足網易內各個產品的使用需求,所以網易內大量的MySQL實際是掛在了自研的分佈式數據庫DDB的下面,DDB封裝了分庫分表的能力,對外提供了可擴展的數據庫處理能力。DDB的實現思路和開源的MyCat、ShardingSphere很像,這裏就不贅述了。

文章後續的多活方案都以MySQL作爲對象描述。

四、機房內的多活

機房內的數據庫多活其實是單機房內數據庫的高可用方案。一個主節點提供讀寫能力,一個高可用從作爲主實例的備份,還有一個只讀從對外提供只讀的能力。這裏三節點的部署方式能夠保證任何一個節點不可用時,都有備用節點接替其提供服務,而整個系統的處理能力不會下降。

這個方案適用發展初期的業務,提供了宿主機級別的容災能力與讀寫分離的能力。

除了異步複製、半同步複製外,MySQL從5.7之後便提供了MySQL Group Replication這種新型的複製模式,相較於傳統的主從方案,MGR提供了自動選主、數據強一致和主從皆可寫入等能力。MGR在網易內部大量產品上都有不錯的推廣。

無論是主從複製還是MGR都逃不開一個複製延遲的問題,MySQL複製的是Binlog,Binlog發送到從節點到Binlog在從節點上回放執行有一段時間差,這就是複製延遲。

在主從切換時,從節點必須回放完堆積的Binlog才能對外提供服務,所以過大的複製延遲會導致不可控的主從切換時間。另外只讀節點上覆制延遲過大,也會導致業務讀到過時的數據,可能影響到業務的正確性。

MySQL後續版本提升了其並行複製的能力,但仍然沒能完全解決複製延遲過高的問題,我們的做法是監控各個實例的TPS情況,當達到可能觸發複製延遲時就對集羣做水平拆分(通過DDB的擴容完成)。

業務上的話DDB提供了LOADBALANCE這樣的hint語法,允許業務指定SQL能容忍的複製延遲,如果延遲超過設定的閾值,則會調度到主節點執行。

五、跨機房多活

如果業務有跨機房容災的需求,或單一機房內資源已經無法滿足業務的發展需求時就有跨機房多活的需求了。最完善的方案是使用單元化來做,但是單元化對業務的改造代價過大,所以出現了一種折中的跨機房多活方案。

此方案中業務層代碼還是在每個機房部署一套,但是在數據庫的寫操作和對一致性要求較高的讀請求會路由到主機房,而每個機房都有自己的只讀實例提供一致性要求不太高的讀請求。

此方案提供了簡單的跨機房容災能力,但是跨機房的讀寫會造成較大的延遲,且對機房間的穩定性要求也較高。

分享前我簡單搭了一個實驗環境,測試了在相同的併發下不同的延遲對數據庫性能的影響。

當延遲上升到3ms時,數據庫性能下降在30%+,當延遲上升到20ms時,數據庫性能下降在75%+。而3ms和20ms剛好是同城多機房和跨城多機房的一個延遲標準。

從數據上看,同城情況下,強一致的同步方案仍然是可行的,而跨城的情況下,基本只能用異步的同步方案了。

六、跨機房單元化

應用仍然在每個機房對立部署,但是機房內的應用只讀寫本機房的數據庫,爲了避免多寫帶來的數據衝突,每個機房只寫自己負責的數據,這就是跨機房單元化多活。這個方案中最重要的就是對流量的路由,每個機房負責了一部分業務流量,常見的劃分方式爲按照ID Hash劃分,或者按照地域劃分。

此方案解決了上面方案跨機房讀寫的延遲問題,並且具有更好的水平擴展性,唯一的弊端是業務改造代價較大。數據庫層要提供較好的數據同步能力,緩存、消息隊列、RPC等組件都要做對應的改造。

不同單元的數據庫雙向同步工作我們使用自研的網易數據運河(NDC)來完成。NDC對外主要提供了數據遷移/數據同步和數據訂閱的能力,數據同步將關係型數據庫的數據實時同步到異構的對端數據庫或數倉中,數據訂閱則將數據庫的增量變更推送進消息隊列,供業務方或流計算任務消費。

數據庫單元化場景下主要用到NDC的雙向同步能力。

七、解決迴環複製

雙向同步第一個要解決的問題即是迴環複製。如果對同步的數據不加過濾仍由其在不同單元的數據庫中來回複製必然消耗大量的網絡帶寬,最終還會導致數據不一致。

解決迴環複製的核心是如何給每個增量變更標註其發生的機房,然後在同步時避免將其同步回發生的機房,就能解決迴環複製的問題了。這裏總結了三種解決迴環複製的方案:

  • 引入額外字段: 給每個同步的表加入額外字段標註上一次對其操作的機房信息,此方案對同步的性能影響最小,切可以做到跨不同數據庫類型使用,但是對業務的表有侵入。
  • GTID: 使用MySQL自己的GTID來標註事務的機房信息,這也是MySQL原生複製解決迴環複製的方案,其對同步的性能影響較小,完全無侵入,但是隻能用於MySQL之間的雙向同步。
  • 事務中引入額外SQL: 在業務的事務中加入額外的SQL來標註此事物產生的機房,此方案也能做到跨數據源,對業務侵入較小,但是對同步的性能影響較大。

三種方案都有各自的優缺點,實現上根據需要選擇即可。NDC對三種方案都做了實現,在單元化場景下實際選擇的是方案一,主要是其使用的額外字段同時是另外一個功能需要的字段,算是重複利用,沒有引入額外開銷。

八、解決數據衝突

正常情況下,每個單元都寫自己負責的數據,不存在數據衝突,但是當某個單元不可用,要做流量切換的時候,就會出現多個單元同時寫入同一行數據的情況,從而產生數據衝突。數據衝突不可避免,我們需要一個機制保證產生衝突的數據最終能保證一致。

解決數據衝突的核心思路是給每個變更賦予一個版本信息,當產生數據衝突時總是保留高版本的數據即可保證最終一致性。NDC採用了最簡單的物理時間作爲變更的版本信息,在進行數據同步時,只有當對端版本低於(或等於)同步的變更時,才同步到對端。

這樣的數據衝突解決方案能解決99%以上的數據衝突,保證數據的最終一致。但是在一些特殊情況下可能失效,一個比較典型的例子就是機房間由於物理時間基準不一致,某個後發生的變更它的版本卻更小,這樣這個變更無法同步到對端機房,最終導致數據不一致。

解決這個問題的辦法是將這種特殊數據版本信息給提升到發生變更之前的版本,保證變更能同步到其他機房。

在實踐中我們發現單個機房內對同一行數據的併發修改也可能造成版本後退,當刪除和插入操作同時進行時也可能出現版本未知的情況,對於這樣的特殊場景都只能通過調整特殊的同步手段最終來確保數據的一致性。

九、實時數據校驗

數據校驗對一個數據同步系統是一個可選但又是必須的功能。在單元化的雙向同步功能中我們不僅使用定期的全量校驗發現系統中存在的不一致風險,還提過了實時數據校驗功能更實時地發現數據不一致的風險。數據校驗的覆蓋範圍和對數據庫的性能影響是一個相悖的問題,更大的校驗覆蓋範圍必將導致更大的數據庫性能開銷。產品上我們允許用戶選擇實時數據校驗的範圍,可選的範圍包括:

  • 雙寫數據校驗:所有在多個單元都發生了寫入的數據都進行校驗。
  • 衝突數據校驗:產生了數據衝突的雙寫數據才進行校驗。
  • 風險數據校驗:存在版本回退這樣的特殊數據才進行數據校驗。

數據校驗的實現比較簡單,將需要校驗的數據放入隊列中,校驗線程不斷地去取需要校驗的數據,並比對源和目標的數據是否一致即可。

十、總結

網易數據庫多活經歷了機房內多活、跨機房多活和跨機房單元化多活三個階段。在數據庫多活上積累了一些經驗,之後也還有更長的路要走。希望這些經驗能夠給各位一些啓發,也期待和大家在數據庫多活的話題上有更多的交流。

Q&A

Q1:數據變化是通過什麼工具推送到Kafka?

A: 通過網易內部自研的數據傳輸工具NDC完成,它的工作原理與開源的Canal類似,模擬自己爲一個MySQL從節點,與主節點建立複製協議獲取binlog,解析後得到行變更數據然後推送入Kafka。

Q2:請問網易的單元化是分了兩個單元嘛?

A: 是的,現在是在杭州的義橋與東冠機房做同城雙單元,機房延遲大概在2ms-3ms之間。

Q3:什麼時候用中間件複製,什麼時候用數據庫自帶複製?

A: 原生複製是MySQL自身提供的複製能力,好處是不需要額外的組件部署,也很穩定。但是當需要進行跨大版本複製,原生複製的性能不滿足需求,或者想自定義一些複製特性的時候就可以使用中間件來完成複製工作了,中間件複製另外的一個優點是可以自定義很多監控指標,實時監控複製的狀態。

Q4:數據同步異常後校驗機制落後於業務怎麼辦?

A: 我猜問題是想問在校驗機制發現數據不一致問題之前,錯誤數據已經暴露給業務方時如何處理。由於校驗並發現問題距離問題發生肯定是有延後的,所以這個問題沒法避免,核心還是在發現問題後能夠快速定位這些不一致數據對業務產生的影響,並根據實際情況來處理。

Q5:單元化具有擴展性,怎麼理解?

A: 單元化的方案對單元的個數理論是沒有限制的,在業務規模上來後發展到幾十個、上百個單元都是可行的。

Q6:剛剛物理時間不一致,數據回退怎麼解決的?

A: 由於不同機房物理時間基準存在偏差,可能在數據更新時發生更新後的數據時間戳低於更新前的時間戳,光從時間戳上看是發生了版本(物理時間即爲數據的版本)回退,此時我們是通過將更新後的時間戳提升到與更新前的時間戳一樣的大小,來保證此條數據能正確同步到對端單元。

Q7:說數據異常了,而業務在校驗發現前已經用了髒數據怎麼辦?

A: 單元化方案裏的雙向同步策略只保證最終一致性,如果中間數據出現短暫的異常讀,業務上是能容忍的,這裏牽扯出一個問題,什麼樣的業務模塊適合做單元化,我們這裏一般認爲更新不是特別頻繁,且業務上自己有比較好的分區邏輯的業務適合做單元化。

Q8:流量切換的時候,怎麼保證業務正確性?

A: 在計劃內切換的場景下,上層流量控制可以保證在底層同步組件都完成數據同步後再將流量切過來,這個過程一般在1分鐘以內,在計劃外的切換過程中,無法做這個保證,所以可能產生兩邊數據不一致,這個需要在事後找出不一致的數據,並按照業務自身特點進行修復。

Q9:支付類數據,如何保證強一致的?

A: 支付類的數據爲了保證強一致性,一般使用MGR這種類Paxos協議來完成數據同步。

Q10:這種數據的同步是解析的binlog,然後寫入NDC同步到異地機房嗎?

A: 是的,具體可以參考Q1的回答。

Q11:系統重構、數據庫表設計及結構都不一致,一般遷移用什麼ETL工具?

A: 網易自研的NDC就能滿足這類需求,開源的話Canal提供了比較完善的binlog複製解析能力,但是一些具體的複雜需求可能需要自己實現。

Q12:網易少量的Oracle都用在哪些業務系統上?如何做多機房雙活的?

A: Oracle現在基本就只有網易支付在用了,據我所知他們並沒有在Oracle上做多機房雙活的計劃。

Q13:NDC支持異構數據庫同步嗎,SQL Server Oracle MySQL?

A: 支持,現在支持的系統暫時只包括Oracle和MySQL,主要還是網易內部沒有使用SQL Server。

Q14:Redis,MQ複製用什麼中間件?

A: Redis據我所知是使用的Redis Cluster方案,MQ的複製是業務自研的一個同步工具。

Q15:MGR怎麼處理DDL?

A: MGR對於DDL的複製和對於普通事務的複製沒有太大區別,不過如果是大表的阻塞性DDL,由於執行時間過長,可能造成較大的複製延遲,網易內部對於大表的DDL一般是用PT-OSC這樣的在線修改表結構工具完成。

Q16:一個單元就是一個閉環的機房嗎?

A: 一般是的,單元化方案裏一般要求流量儘量在單元內閉環。

Q17:一個交易,做到一半,掛了,流量切換後,是把交易重新做嗎?之前做的怎麼處理?

A: 交易一般具有事務性,所以做到一半掛了一般理解爲這個事務沒有提交,切換後整個過程需要重做。

Q18:如果解決分庫分表後,數據一致性的問題?本來應該放在一個事務的兩個SQL,分庫分表後寫往兩個實例?有哪些方法可以解決,除了分佈式事務,性能太低?

A: 分佈式數據庫中的事務基本都是按分佈式事務的方式解決的,比較經典的是兩階段提交,如果覺得兩階段提交效率太低,可以業務層使用基於MQ的柔性事務方案。

Q19:版本提升的依據是什麼呢?怎麼判斷是不是時間戳延遲引起的?

A: 版本回退的判斷即是通過binlog中的時間戳列的數據得到的,所以依據就是binlog中的數據。

Q20:MGR能用在異地多活場景嗎?延時太大吧?對網絡有哪些要求?

A: 可以用在同城單元化的方案裏,同城的延遲情況對性能的影響較爲可控,同城延遲一般在3ms以下。

Q21:Canal+RocketMQ可以用在異地複製嗎?

A: 可以。

Q22:銀行級的場景下使用哪種方案做多活好啊?

A: 銀行對數據庫一致性要求比較高,除了一些強一致的商業方案外,MySQL的MGR是一個不錯的選擇。

Q23:流量切換之前,做個一個操作。切換後,發現數據沒同步過來,又做了,那不是做了兩次嗎?

A: 答案參考問題8,計劃內和計劃外可能要分開討論。

Q24:MGR 和 RAC有比較過嗎 同城雙活部署的話 MGR是否可以達到RAC的同步能力?

A: MGR本質上還是日誌的同步,和RAC存儲共享的思路是完全不一樣的,性能上MGR也肯定是不如RAC的,但是好像MGR部署門檻低。

Q25人工做DDL,那人比數據同步慢怎麼辦?數據過來了,但是表還沒改過來,就出錯了。

A: 流程上一定是DDL在所有單元都執行完成後再開放給業務使用。

Q26:MGR多活現在有在覈心業務上生產的嗎?

A: 網易內部暫時還沒有。

作者介紹

周勁松

網易杭州研究院資深研發工程師

  • 來自網易數據科學中心,目前是網易分佈式數據庫DDB及網易數據運河NDC項目負責人。
  • 對數據庫及相關中間件的設計和研發有豐富經驗。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650791482&idx=1&sn=b067a153deba0783bf3fd8308296b1bf&chksm=f3f96bafc48ee2b99beb3c2a5816a7f81254fc7acac9ca245ad7173fbb27905c74fee94315a6&scene=27#wechat_redirect

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章