冗餘表數據一致性

本文主要討論四個問題:數據庫

(1)爲何會有冗餘表的需求架構

(2)如何實現冗餘表異步

(3)正反冗餘表誰先執行工具

(4)冗餘表如何保證數據的一致性優化

 

1、需求緣起spa

互聯網不少業務場景的數據量很大,此時數據庫架構要進行水平切分,水平切分會有一個patition key,經過patition key的查詢可以直接定位到庫,可是非patition key上的查詢可能就須要掃描多個庫了。3d

例如訂單表,業務上對用戶和商家都有訂單查詢需求:日誌

Order(oid, info_detail)blog

T(buyer_id, seller_id, oid)事務

若是用buyer_id來分庫,seller_id的查詢就須要掃描多庫。

若是用seller_id來分庫,buyer_id的查詢就須要掃描多庫。

 

這類需求,爲了作到高吞吐量低延時的查詢,每每使用「數據冗餘」的方式來實現,就是文章標題裏說的「冗餘表」

T1(buyer_id, seller_id, oid)

T2(seller_id, buyer_id, oid)

同一個數據,冗餘兩份,一份以buyer_id來分庫,知足買家的查詢需求;

一份以seller_id來分庫,知足賣家的查詢需求。

 

2、冗餘表的實現方案

【方法一:服務同步寫


顧名思義,由服務層同步寫冗餘數據,如上圖1-4流程:

1)業務方調用服務,新增數據

2)服務先插入T1數據

3)服務再插入T2數據

4)服務返回業務方新增數據成功

優勢

1)不復雜,服務層由單次寫,變兩次寫

2)數據一致性相對較高(由於雙寫成功才返回)

缺點

1)請求的處理時間增長(要插入次,時間加倍)

2)數據仍可能不一致,例如第二步寫入T1完成後服務重啓,則數據不會寫入T2

 

若是系統對處理時間比較敏感,引出經常使用的第二種方案

【方法二:服務異步寫


數據的雙寫並再也不由服務來完成,服務層異步發出一個消息,經過消息總線發送給一個專門的數據複製服務來寫入冗餘數據,如上圖1-6流程:

(1)業務方調用服務,新增數據

(2)服務先插入T1數據

3)服務向消息總線發送一個異步消息(發出便可,不用等返回,一般很快就能完成)

4)服務返回業務方新增數據成功

5)消息總線將消息投遞給數據同步中心

6)數據同步中心插入T2數據

優勢

1)請求處理時間短(只插入1次)

缺點

(1)系統的複雜性增長了,多引入了一個組件(消息總線)和一個服務(專用的數據複製服務)

(2)由於返回業務線數據插入成功時,數據還不必定插入到T2中,所以數據有一個不一致時間窗口(這個窗口很短,最終是一致的)

(3)在消息總線丟失消息時,冗餘表數據會不一致

 

若是想解除「數據冗餘」對系統的耦合,引出經常使用的第三種方案

【方法三:線下異步寫


數據的雙寫再也不由服務層來完成,而是由線下的一個服務或者任務來完成,如上圖1-6流程:

(1)業務方調用服務,新增數據

(2)服務先插入T1數據

3)服務返回業務方新增數據成功

4)數據會被寫入到數據庫的log

5)線下服務或者任務讀取數據庫的log

6)線下服務或者任務插入T2數據

優勢

(1)數據雙寫與業務徹底解耦

(2)請求處理時間短(只插入1次)

缺點

(1)返回業務線數據插入成功時,數據還不必定插入到T2中,所以數據有一個不一致時間窗口(這個窗口很短,最終是一致的)

(2)數據的一致性依賴於線下服務或者任務的可靠性

 

上述三種方案各有優缺點,但無論哪一種方案,都會面臨「究竟先寫T1仍是先寫T2」的問題?這該怎麼辦呢?

 

3、究竟先寫正表仍是反表

對於一個不能保證事務性的操做,必定涉及「哪一個任務先作,哪一個任務後作」的問題,解決這個問題的方向是:

【若是出現不一致】,誰先作對業務的影響較小,就誰先執行。

 

以上文的訂單生成業務爲例,buyer和seller冗餘表都須要插入數據:

T1(buyer_id, seller_id, oid)

T2(seller_id, buyer_id, oid)

用戶下單時,若是「先插入buyer表T1,再插入seller冗餘表T2」,當第一步成功、第二步失敗時,出現的業務影響是「買家能看到本身的訂單,賣家看不到推送的訂單」

相反,若是「先插入seller表T2,再插入buyer冗餘表T1」,當第一步成功、第二步失敗時,出現的業務影響是「賣家能看到推送的訂單,買家看不到本身的訂單」

因爲這個生成訂單的動做是買家發起的,買家若是看不到訂單,會以爲很是奇怪,而且沒法支付以推進訂單狀態的流轉,此時即便賣家看到有人下單也是沒有意義的。

所以,在此例中,應該先插入buyer表T1,再插入seller表T2。

 

however,記住結論:若是出現不一致】,誰先作對業務的影響較小,就誰先執行。

 

4、如何保證數據的一致性

從二節和第三節的討論能夠看到,無論哪一種方案,由於兩步操做不能保證原子性,總有出現數據不一致的可能,那如何解決呢?

【方法一:線下掃面正反冗餘表所有數據】


如上圖所示,線下啓動一個離線的掃描工具,不停的比對正表T1和反表T2,若是發現數據不一致,就進行補償修復

優勢

(1)比較簡單,開發代價小

(2)線上服務無需修改,修復工具與線上服務解耦

缺點

(1)掃描效率低,會掃描大量的「已經可以保證一致」的數據

(2)因爲掃描的數據量大,掃描一輪的時間比較長,即數據若是不一致,不一致的時間窗口比較長

 

有沒有隻掃描「可能存在不一致可能性」的數據,而不是每次掃描所有數據,以提升效率的優化方法呢?

【方法二:線下掃描增量數據】


每次只掃描增量的日誌數據,就可以極大提升效率,縮短數據不一致的時間窗口,如上圖1-4流程所示:

(1)寫入正表T1

(2)第一步成功後,寫入日誌log1

(3)寫入反表T2

(4)第二步成功後,寫入日誌log2

固然,咱們仍是須要一個離線的掃描工具,不停的比對日誌log1和日誌log2,若是發現數據不一致,就進行補償修復

優勢

(1)雖比方法一複雜,但仍然是比較簡單的

(2)數據掃描效率高,只掃描增量數據

缺點

(1)線上服務略有修改(代價不高,多寫了2條日誌)

(2)雖然比方法一更實時,但時效性仍是不高,不一致窗口取決於掃描的週期

 

有沒有實時檢測一致性並進行修復的方法呢?

【方法三:實時線上「消息對」檢測】


此次不是寫日誌了,而是向消息總線發送消息,如上圖1-4流程所示:

(1)寫入正表T1

(2)第一步成功後,發送消息msg1

(3)寫入反表T2

(4)第二步成功後,發送消息msg2

此次不是須要一個週期掃描的離線工具了,而是一個實時訂閱消息的服務不停的收消息。

假設正常狀況下,msg1和msg2的接收時間應該在3s之內,若是檢測服務在收到msg1後沒有收到msg2,就嘗試檢測數據的一致性,不一致時進行補償修復

優勢

(1)效率高

(2)實時性高

缺點

(1)方案比較複雜,上線引入了消息總線這個組件

(2)線下多了一個訂閱總線的檢測服務

 

however,技術方案自己就是一個投入產出比的折衷,能夠根據業務對一致性的需求程度決定使用哪種方法。我這邊有過好友數據正反表的業務,使用的就是方法二。

相關文章
相關標籤/搜索