文章目錄
- 概要
- 問題解析
- 思考問題
- 數據基礎
- 讀取
- 寫入
- 總結
概要
說起IM程序我們都不陌生,本篇文章我們就為如何實現一個IM做一個簡單的整體方案設計以及基本的數據結構
問題解析
我們先不上一大堆牛逼哄哄的中間件。
我們先從實現角度,來講講設計思路。
從整體來看客戶端主要做兩件事 ,接受消息,發送消息。
從接受消息的角度來說,客戶端主要也只做兩件事 ‘被動收到推送消息(PUSH)’與‘主動拉取消息(PULL)’
我們簡單的分析下,這三件事的基本操作是什么?
發送消息:將用戶訊息傳輸到服務器
推送消息:‘實時’將用戶信息傳輸到服務器
拉取數據:將用戶歷史數據傳輸給用戶
思考問題
其實我們思考下上文所說的三個操作,除了推送消息是服務器需要判斷用戶當前是否在線,進行操作。發送消息,拉取數據。其實就是往數據庫一寫一查的事。
在發送消息的時候,我們將用戶數據存入數據庫。‘此時判斷目標用戶是否在線,如果是則進行推送。‘(當然通知訂閱的方式也是可以的,這只是舉一個例子)。等到用戶進行查詢的時候將其查出。
問題很簡單,甚至初學者都能上手。
問題就是當這張表在高并發的時候,會有大量的寫以及大量的讀。以及隨著時間,數據表不斷增長,甚至短時間內就瘋狂增長。此時應該如何處理?
解決方案有很多,因此我們就有了各式各樣的中間件。本篇文章的中心思想也不是要教大家如何用這寫組件。
我們只需要打好基礎,后續隨著業務驅動,來決定技術的走向。
數據基礎
講到IM數據結構,很簡單最簡單不超過五個字段
字段 |
---|
id |
senderId |
readerId |
content |
sendTime |
isSend |
最重要的就是這個id,我們需要他能做到,有序,唯一。
讀取
為什么,我們想想如何讀取用戶的未讀信息?總共三種
- 讀取所有未讀的消息然后進行分頁( AND isSend= 2 ORDER BY sendTime desc limit 500)
- 根據傳入的最后接收時間獲取( AND sendTime >“” ORDER BY sendTime desc limit 500)
- 根據傳入的最后ID進行獲取(AND id > “XXX” ORDER BY id desc limit 500)
第一種方案,強依賴isSend=2,查詢效率十分緩慢
第二種方案,根據sendTime 可能因為時間重復會發生數據丟失,并且增加后續業務開發的設計難度。
只有第三種方案是設計成本最低,并且在查詢性能之上最高效,拓展能力也是最好。可以最大層面的滿足后續業務需求。
寫入
ID方案的主要設計復雜難度在于‘寫入’。
因為我們在做其他業務的時候,我們都知道,一旦涉及有序且唯一。我們就很難逃開分布式鎖。但是分布式鎖+唯一則會嚴重影響效率。因為只要并發夠多,最算你數據插入速度再快。也會形成嚴重的排隊問題。
但是其實我們無需悲觀,這時候我們就需要想想。
雖然我們表面說是要有序,先進來的請求他的id不應該小于后進來的。
但是真的需要做的那么精準嗎?
其實不需要的,其實并不需要。在一秒之內同時進來的數據,用戶是無感的。因此隨先生成id又有什么問題呢?
數據庫內的數據他的id真的需要實現從上到下升序嗎
其實也不需要,只要id能表明他的有序性,誰先進個又有何分別呢?(當然了,你晚個一兩秒,在讀取時形成數據丟失就不好了)
因此,我們只需要依賴雪花算法,在形成id的那一刻能表明請求的先后順序便可。
總結
到此,我們就形成了一個最基本的IM設計方案。
在后續開發中,我們遇上數據表大,我們可以使用分庫分表。插入數據效率慢,我們可以使用插入緩沖等等。
我們只需要在我們的基礎上不斷添磚加瓦就好