全球異地多活架構設計(二): 數據層的支持

要做到全球異地多活, 一定要在數據層支持多機房寫入, 並且對大多數業務場景提供最終一致性的解決方案。原因如下:

  • 跨洲的網絡延遲在100ms的數量級,如果只有單點寫, 對於用戶體驗是種災難
  • 對於高頻操作來說, 如果做強一致性,那麼任然受限於網絡延遲, 對於用戶體驗是種災難

既然決定要選擇最終一致性, 那麼隨之而來就有兩個問題需要解決:

  1. 跨機房的數據同步
  2. 多點寫入時的數據衝突處理

一 、數據同步

數據的同步有幾個核心問題需要考慮:

  1. 獲取數據變更以及重放
  2. 不丟不重不亂序
  3. 避免數據迴環同步

獲取數據變更以及重放

這個問題比較好解決, 可以通過存儲提供的增量日誌(比如mysql的binlog,redis的AOF)來獲取本機房的數據變更。 如果用到的存儲沒有提供這個功能, 也可以考慮在業務層做類似的事情。 比如寫入成功後, 把變更操作發到消息隊列。(但是對於數據一致性要求比較高的場景, 要考慮到[變更存儲+發消息]這個操作的事務性,很麻煩)。

一般來說,我們用的存儲都會提供主從方案,所以思路都是通過fake成主存儲的一個slave來獲取數據變更
獲取到變更之後,可以寫入消息隊列再mirror到別的DC(比如Kafka MirrorMaker), 在別的DC重放這些變更。

不丟不重不亂序

這時候引入了新問題, 消息的寫入和消費,是否需要exactly once和消息的有序?

這個需要具體問題具體分析,比如mysql的binlog會帶上數據before和after的鏡像,因此我們可以接受消息的重複,只需要保證at least once即可; 而對redis而言, set操作可以接受重複, 但是incr等就不能接受。至於如何做到exactly once,不在本文討論的範圍內, 有興趣的同學可以參考: https://www.confluent.io/blog/exactly-once-semantics-are-possible-heres-how-apache-kafka-does-it/

但是一般來說, 消息的有序都是需要的。我們無法容忍[set a=v1, set a=v2]的序列被處理成[set a=v2, set a=v1]。 這個問題相對比較簡單,假設我們用kafka做消息隊列, 那麼只要用key做partitioner即可做到這一點。

避免數據迴環同步

按我們目前所討論的方案, 獲取變更日誌之後再重放, 那麼一條變更就會在多個機房之間來回同步, 也就是產生了迴環問題。

那麼如何解決迴環問題呢? 其實思路很簡單, 就是提供一個機制,讓我們在解析重放日誌時, 可以判斷這個變更是否來自本機房。

以mysql爲例, 我們可以利用mysql的事務機制, 在事務的開頭和結尾插入同步標誌, 在解析時,發現有這個同步標誌, 就過濾掉, 不同到別的機房。 落實到具體實現的話, 則是在同步的數據庫中創建輔助表, 每次從對端機房同步過來的數據, 都在事務的開頭和結尾對輔助表進行相關標誌的update操作, 這樣就可以在binlog中做區分了。

二、衝突處理

既然我們允許多個數據中心對一條數據進行寫入, 那麼必然會產生這麼一種情況: 在數據中心A對數據X進行變更,V從v0->v1 , 當我們把這個變更同步到數據中心B時, 我們期望update X from v0 to v1, 結果發現在數據中心B,X的值已經是v1’了, 這時候, 就產生了數據衝突。 爲了多機房數據的一致性,我們需要處理這種衝突。

LWW策略

比較經典的策略是Last Write Wins, 具體的說, 就是爲數據的變更加上時間戳, 在同步到別的機房時,如果發現有衝突, 則比較時間戳, 選取時間戳大的那個版本。

這種策略需要注意的問題是,本地機器的時間是不準確的, 各個機器生成的時間戳的大小可能和真實世界中的變更順序不一致。

Google Spanner利用原子鐘和GPS提供了TrueTime API(可以理解爲一個全局時鐘), 全球各個數據中心的spanserver產生的時間戳都是基於同一參考系,是單調遞增的。(不過也不是完全準確,它保證誤差在一個範圍之內,詳細內容可見: https://ai.google/research/pubs/pub39966 )

邏輯主DC

有了LWW就夠了嗎? 當然不是。 即使概率很低, 仍然可能發生兩個數據的變更時間戳完全一致的情況。 因此我們需要在多個DC中指定一個邏輯主DC, 發生時間戳完全一致的情況時, 用邏輯主DC的數據覆蓋別的DC。

衝突報告訂閱

對於一些敏感數據的變更(比如facebook需要下線一些恐怖主義的帖子),當發生數據衝突時,則不能簡單的通過時間戳來決定選擇哪個數據版本。 否則,可能會發生被審覈人員下線的帖子, 又被重新放出的情況。

這時候,我們需要提供數據衝突報告, 比如在發生數據衝突時,把衝突情況發送到消息隊列, 下游根據具體的業務邏輯來處理這種衝突, 決定選用哪個版本的數據。

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