來源:https://www.toutiao.com/i6686735232772604429
一朋友和我討論他前段時間面試某大公司的一題目 :
企業IM比如企業微信、釘釘里面的群訊息的有個已讀未讀的功能,發送者剛發出訊息時,當前群里其他群成員都是未讀狀態,陸陸續續有人看了這個訊息,這時候訊息的詳情變成x人已讀,y人未讀,如下圖所示,有具體的已讀未讀串列(萬惡的功能,看到同事or老板的訊息不能假裝沒看到了),每條訊息對應一個唯一的messageid(uint64_t),每個用戶對應一個唯一的userid(uint64_t),應該如何保存這個訊息對應的已讀未讀詳情呢?

我第一時間給出一個很簡單粗暴的方案:
對于每一個messageid,存當前readids + unreadids,當群成員A已讀某一條訊息時,把A userid從unreadids移除寫到readids上就好了,客戶端更新到messageid對應的詳情串列,就可以展示m人已讀,n人未讀
顯然這么簡單粗暴的方案面試官是不會滿意的,追問有沒有更好的方案呢?
仔細分析,按照目前的設計,每一條訊息,已讀未讀詳情就要占用8B * 群成員數的記憶體,如果一個活躍的200人大群,每發一條訊息,已讀未讀就要1600B,如果平均每天訊息量是1k,那每個這樣的群,每天就要1.6MB磁盤空間,對于客戶端來說,特別是手機端,占用磁盤空間是用戶不能接受的,又不能把作業訊息刪了,對于服務器端來說,用戶群體如果特別大,那資料庫存盤這個成本也不小
其實未讀已讀就是一個0/1的標記而已,可以維護一個bitmap來實作呢?具體應該怎么做呢?
群元資訊保存userid到自增mapid的映射
struct UserInfo
{
uint64_t userid;
uint32_t mapid;
};
struct GroupMetaInfo
{
vector <UserInfo> members;
string name;
uint32_t maxid;
// other info
};
這樣群成員每加入一個群里,就有mapid<->usreid的雙向映射了,假如群里有5個成員ABCDE, 那就對應mapid 1-5,messageid對應的訊息詳情存盤就可以設計成
{ uint32_t maxid, uint8_t readbit[]}
如上面的案例就是{5, readbit[0] =bin(0000 0000)}; 就占用了5B(4+1),A發訊息,D已讀訊息時,就更新成{5,readbit[0]= bin(0000 1000)},其余4人都已讀訊息時 更新為{5, readbit[0]=bin(0001 1110)}
這是個粗略的方案,里面還有一些細節值得思考:
- 退出的成員呢?比如C退出群,發訊息時maxid還是5,已讀+未讀總人數應該是3(不包括發訊息者本人),目前資訊只有5個bit(0/1),識別不出來誰已經退出群聊了
- 退出群聊的成員如何處理?從GruopMetaInfo里面洗掉么?退出群聊成員重新加入又如何分配id呢?
首先2這個點,退出群聊的成員只能標記洗掉,不能物理洗掉,不然客戶端展示已讀未讀詳情時,通過mapid找不到對應的userid,退出的成員又重新加入群聊這個就好辦了,把標記洗掉改成非標記洗掉,還是用舊的mapid
至于1呢? 我目前想到比較好的方式就是再加多一個bitmap,記錄成員在訊息發送時是否已經退出群聊了,退出群聊就置為1, 所以最終方案就是
群資訊增加userid,自增mapid雙向映射,退出群聊成員標記洗掉,messageid 已讀未讀詳情存盤 {maxid, readbit[], quitbit[]}
新的方案帶來怎樣的收益呢?
- 增加自增mapid欄位,一個群聊維護一份,成本幾乎可以忽略不計
- 每個成員已讀未讀由8B(64bit)優化成2bit,減少62/64, 200人已讀未讀舊的方案1600B, 現在只需要(200/8) * 2 + 4 = 54 , 每條訊息節約95%+
如果maxid如果到百萬甚至千萬級別,那豈不是災難?一般實際場景,群聊是會限制人數的,就算不斷踢人加新人,那maxid最多也只能到企業人數, 如果maxid達到一個特別大數字,已讀未讀對應的存盤可以增加多一個flag,如果bitmap存盤成本遠超過最初的方案,可以用最初的方案來實作,客戶端提前埋好兼容邏輯就可以了
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
2.勁爆!Java 協程要來了,,,
3.Spring Boot 2.x 教程,太全了!
4.Spring Boot 2.6 正式發布,一大波新特性,,
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/417036.html
標籤:Java
