GitChat · 架構 | 從好友中心開始,聊「多對多」類業務數據庫水平切分架構實踐

來自 GitChat 做者:沈劍
更多IT技術分享,盡在微信公衆號:GitChat技術雜談git

前言

本文將以「好友中心」爲例,介紹「多對多」類業務,隨着數據量的逐步增大,數據庫性能顯著下降,數據庫水平切分相關的架構實踐。數據庫

1、什麼是多對多關係

所謂的「多對多」,來自數據庫設計中的「實體-關係」ER模型,用來描述實體之間的關聯關係,一個學生能夠選修多個課程,一個課程能夠被多個學生選修,這裏學生與課程時間的關係,就是多對多關係。微信

2、好友中心業務分析

好友關係主要分爲兩類,弱好友關係強好友關係,兩類都有典型的互聯網產品應用。架構

弱好友關係的創建,不須要雙方彼此贊成異步

  • 用戶A關注用戶B,不須要用戶B贊成,此時用戶A與用戶B爲弱好友關係,對A而言,暫且理解爲「關注」;
  • 用戶B關注用戶A,也不須要用戶A贊成,此時用戶A與用戶B也爲弱好友關係,對A而言,暫且理解爲「粉絲」;

微博粉絲是一個典型的弱好友關係應用。數據庫設計

強好友關係的創建,須要好友關係雙方彼此贊成分佈式

  • 用戶A請求添加用戶B爲好友,用戶B贊成,此時用戶A與用戶B則互爲強好友關係,即A是B的好友,B也是A的好友。

QQ好友是一個典型的強好友關係應用。工具

enter image description here

好友中心是一個典型的多對多業務,一個用戶能夠添加多個好友,也能夠被多個好友添加,其典型架構爲:性能

enter image description here

  • friend-service:好友中心服務,對調用者提供友好的RPC接口
  • db:對好友數據進行存儲

3、弱好友關係-元數據簡版實現

經過弱好友關係業務分析,很容易瞭解到,其核心元數據爲:學習

guanzhu(uid, guanzhu_uid);

fensi(uid, fensi_uid);

其中:

  • guanzhu表,用戶記錄uid全部關注用戶guanzhu_uid
  • fensi表,用來記錄uid全部粉絲用戶fensi_uid

須要強調的是,一條弱關係的產生,會產生兩條記錄,一條關注記錄,一條粉絲記錄

例如:用戶A(uid=1)關注了用戶B(uid=2),A多關注了一個用戶,B多了一個粉絲,因而:

  • guanzhu表要插入{1, 2}這一條記錄,1關注了2
  • fensi表要插入{2, 1}這一條記錄,2粉了1

如何查詢一個用戶關注了誰呢?

回答:在guanzhu的uid上創建索引:

select * from guanzhu where uid=1;

便可獲得結果,1關注了2。

如何查詢一個用戶粉了誰呢?

回答:在fensi的uid上創建索引:

select * from fensi where uid=2;

便可獲得結果,2粉了1。

4、強好友關係-元數據實現一

經過強好友關係業務分析,很容易瞭解到,其核心元數據爲:

friend(uid1, uid2);

其中:

  • uid1,強好友關係中一方的uid
  • uid2,強好友關係中另外一方的uid

uid=1的用戶添加了uid=2的用戶,雙方都贊成加彼此爲好友,這個強好友關係,在數據庫中應該插入記錄{1, 2}仍是記錄{2,1 }呢

回答:均可以,爲了不歧義,能夠人爲約定,插入記錄時uid1的值必須小於uid2。

例如:有uid=1,2,3三個用戶,他們互爲強好友關係,那邊數據庫中多是這樣的三條記錄

{1, 2}

{2, 3}

{1,3 }

如何查詢一個用戶的好友呢?

回答:假設要查詢uid=2的全部好友,只需在uid1和uid2上創建索引,而後:

select * from friend where uid1=2

union

select * from friend where uid2=2

便可獲得結果。

做業:爲什麼不使用這樣的SQL語句呢?

select * from friend uid1=2 or uid2=2

供你們思考。

5、強好友關係-元數據實現二

強好友關係是弱好友關係的一個特例,A和B必須互爲關注關係(也能夠說,同時互爲粉絲關係),即也可使用關注表和粉絲表來實現:

guanzhu(uid, guanzhu_uid);

fensi(uid, fensi_uid);

例如:用戶A(uid=1)和用戶B(uid=2)爲強好友關係,即相互關注:
用戶A(uid=1)關注了用戶B(uid=2),A多關注了一個用戶,B多了一個粉絲,因而:

  • guanzhu表要插入{1, 2}這一條記錄
  • fensi表要插入{2, 1}這一條記錄

同時,用戶B(uid=2)也關注了用戶A(uid=1),B多關注了一個用戶,A多了一個粉絲,因而:

  • guanzhu表要插入{2, 1}這一條記錄
  • fensi表要插入{1, 2}這一條記錄

6、數據冗餘是實現多對多關係水平切分的經常使用實踐

對於強好友關係的兩類實現:

  • friend(uid1, uid2)表
  • 數據冗餘guanzhu表與fensi表(後文稱正表T1與反表T2)

在數據量小時,看似無差別,但數據量大時,數據冗餘的優點就體現出來了:

  • friend表,數據量大時,若是使用uid1來分庫,那麼uid2上的查詢就須要遍歷多庫
  • 正表T1與反表T2經過數據冗餘來實現好友關係,{1,2}{2,1}分別存在於兩表中,故兩個表都使用uid來分庫,均只須要進行一次查詢,就能找到對應的關注與粉絲,而不須要多個庫掃描

數據冗餘,是多對多關係,在數據量大時,數據水平切分的經常使用實踐

7、如何進行數據冗餘

接下來的問題轉化爲,好友中心服務如何來進行數據冗餘,常見有三種方法。

方法一:服務同步冗餘

enter image description here

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

  1. 業務方調用服務,新增數據
  2. 服務先插入T1數據
  3. 服務再插入T2數據
  4. 服務返回業務方新增數據成功

優勢

  1. 不復雜,服務層由單次寫,變兩次寫
  2. 數據一致性相對較高(由於雙寫成功才返回)

缺點

  1. 請求的處理時間增長(要插入次,時間加倍)
  2. 數據仍可能不一致,例如第二步寫入T1完成後服務重啓,則數據不會寫入T2

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

方法二:服務異步冗餘

enter image description here

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

  1. 業務方調用服務,新增數據
  2. 服務先插入T1數據
  3. 服務向消息總線發送一個異步消息(發出便可,不用等返回,一般很快就能完成)
  4. 服務返回業務方新增數據成功
  5. 消息總線將消息投遞給數據同步中心
  6. 數據同步中心插入T2數據

優勢

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

缺點

  1. 系統的複雜性增長了,多引入了一個組件(消息總線)和一個服務(專用的數據複製服務)
  2. 由於返回業務線數據插入成功時,數據還不必定插入到T2中,所以數據有一個不一致時間窗口(這個窗口很短,最終是一致的)
  3. 在消息總線丟失消息時,冗餘表數據會不一致

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

方法三:線下異步冗餘

enter image description here

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

  1. 業務方調用服務,新增數據
  2. 服務先插入T1數據
  3. 服務返回業務方新增數據成功
  4. 數據會被寫入到數據庫的log中
  5. 線下服務或者任務讀取數據庫的log
  6. 線下服務或者任務插入T2數據

優勢

  1. 數據雙寫與業務徹底解耦
  2. 請求處理時間短(只插入1次)

缺點

  1. 返回業務線數據插入成功時,數據還不必定插入到T2中,所以數據有一個不一致時間窗口(這個窗口很短,最終是一致的)
  2. 數據的一致性依賴於線下服務或者任務的可靠性

上述三種方案各有優缺點,能夠結合實際狀況選取。

數據冗餘當然可以解決多對多關係的數據庫水平切分問題,但又帶來了新的問題,如何保證正表T1與反表T2的數據一致性呢?

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

上一節的討論能夠看到,無論哪一種方案,由於兩步操做不能保證原子性,總有出現數據不一致的可能,高吞吐分佈式事務是業內還沒有解決的難題,此時的架構優化方向,並非徹底保證數據的一致,而是儘早的發現不一致,並修復不一致

最終一致性,是高吞吐互聯網業務一致性的經常使用實踐。更具體的,保證數據最終一致性的方案有三種。

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

enter image description here

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

優勢

  1. 比較簡單,開發代價小
  2. 線上服務無需修改,修復工具與線上服務解耦

缺點

  1. 掃描效率低,會掃描大量的「已經可以保證一致」的數據
  2. 因爲掃描的數據量大,掃描一輪的時間比較長,即數據若是不一致,不一致的時間窗口比較長

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

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

enter image description here

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

  1. 寫入正表T1
  2. 第一步成功後,寫入日誌log1
  3. 寫入反表T2
  4. 第二步成功後,寫入日誌log2

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

優勢

  1. 雖比方法一複雜,但仍然是比較簡單的
  2. 數據掃描效率高,只掃描增量數據

缺點

  1. 線上服務略有修改(代價不高,多寫了2條日誌)
  2. 雖然比方法一更實時,但時效性仍是不高,不一致窗口取決於掃描的週期

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

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

enter image description here

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

  1. 寫入正表T1
  2. 第一步成功後,發送消息msg1
  3. 寫入反表T2
  4. 第二步成功後,發送消息msg2

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

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

優勢

  1. 效率高
  2. 實時性高

缺點

  1. 方案比較複雜,上線引入了消息總線這個組件
  2. 線下多了一個訂閱總線的檢測服務

however,技術方案自己就是一個投入產出比的折衷,能夠根據業務對一致性的需求程度決定使用哪種方法。

9、總結

文字較多,但願儘可能記住以下幾點:

  • 好友業務是一個典型的多對多關係,又分爲強好友與弱好友 。
  • 數據冗餘是一個常見的多對多業務數據水平切分實踐。
  • 冗餘數據的常見方案有三種:

    • 服務同步冗餘
    • 服務異步冗餘
    • 線下異步冗餘
  • 數據冗餘會帶來一致性問題,高吞吐互聯網業務,要想徹底保證事務一致性很難,常見的實踐是最終一致性。
  • 最終一致性的常見實踐是,儘快找到不一致,並修復數據,常見方案有三種。

    • 線下全量掃描法
    • 線下增量掃描法
    • 線上實時檢測法

10、還有哪些未盡事宜

以訂單中心爲典型的「多KEY」類業務的水平拆分架構又應該怎麼處理,敬請期待下期。

彩蛋

【 做者招募 】

咱們 GitChat 通過半年的運營,在產品形態上不斷改進。

此次,咱們將系統性的優化 GitChat 一些內容,在原有 Chat 基礎上推出新的內容分享產品:

GitQ 精品課程

若是您在 IT技術上有本身獨到的學習心得 或 自成體系的技術成長套路,咱們很是期待您能在 GitQ 上進一步實現:

GitQ 定位:具有專業性、成體系的IT類課程

GitQ 形態:獨家文章(6~12篇)+讀者圈答疑

GitQ 訂價:9.99 / 19.99 等

【做者如何開設 GitQ 精品課?】

  1. 提交以下內容:課程名稱 / 用戶定位 / 課程大綱
  2. GitChat 進行內容評估與課程策劃
  3. 雙方肯定課程細節、交付時間
  4. GitChat 完成課程的上線、宣傳推廣

【做者注意事項】

  1. GitQ 精品課內的文章,在課程上線一年內屬於 GitChat 獨家使用。
  2. 按約定時間準時交付文章。
  3. 課程上線後,積極在讀者圈回答用戶的提問。

【更多諮詢】
在微信公衆號: GitChat技術雜談(GitChat_Club) 留言 ,或加微信聯繫 GitChat 主編:ztx415


實錄:《沈劍:「多對多」類業務數據庫水平切分架構解析》


圖片描述

相關文章
相關標籤/搜索