<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:如何設計群聊消息的已讀未讀功能?

          共 2040字,需瀏覽 5分鐘

           ·

          2022-01-14 01:54

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

          我第一時間給出一個很簡單粗暴的方案:
          對于每一個messageid,存當前readids + unreadids,當群成員A已讀某一條消息時,把A userid從unreadids移除寫到readids上就好了,客戶端更新到messageid對應的詳情列表,就可以展示m人已讀,n人未讀
          顯然這么簡單粗暴的方案面試官是不會滿意的,追問有沒有更好的方案呢?

          仔細分析,按照目前的設計,每一條消息,已讀未讀詳情就要占用8B * 群成員數(shù)的內存,如果一個活躍的200人大群,每發(fā)一條消息,已讀未讀就要1600B,如果平均每天消息量是1k,那每個這樣的群,每天就要1.6MB磁盤空間,對于客戶端來說,特別是手機端,占用磁盤空間是用戶不能接受的,又不能把工作消息刪了,對于服務器端來說,用戶群體如果特別大,那數(shù)據(jù)庫存儲這個成本也不小。

          其實未讀已讀就是一個0/1的標記而已,可以維護一個bitmap來實現(xiàn)呢?具體應該怎么做呢?

          群元信息保存userid到自增mapid的映射:
          struct?UserInfo?
          {?
          ?uint64_t?userid;
          ?uint32_t?mapid;
          };

          struct?GroupMetaInfo?
          {
          ?vector??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發(fā)消息,D已讀消息時,就更新成{5,readbit[0]= bin(0000 1000)},其余4人都已讀消息時 更新為{5, readbit[0]=bin(0001 1110)}。

          這是個粗略的方案,里面還有一些細節(jié)值得思考:
          1. 退出的成員呢?比如C退出群,發(fā)消息時maxid還是5,已讀+未讀總人數(shù)應該是3(不包括發(fā)消息者本人),目前信息只有5個bit(0/1),識別不出來誰已經(jīng)退出群聊了
          2. 退出群聊的成員如何處理?從GruopMetaInfo里面刪除么?退出群聊成員重新加入又如何分配id呢?

          首先2這個點,退出群聊的成員只能標記刪除,不能物理刪除,不然客戶端展示已讀未讀詳情時,通過mapid找不到對應的userid,退出的成員又重新加入群聊這個就好辦了,把標記刪除改成非標記刪除,還是用舊的mapid。

          至于1呢?我目前想到比較好的方式就是再加多一個bitmap,記錄成員在消息發(fā)送時是否已經(jīng)退出群聊了,退出群聊就置為1, 所以最終方案就是:

          群信息增加userid,自增mapid雙向映射,退出群聊成員標記刪除,messageid 已讀未讀詳情存儲 {maxid, readbit[], quitbit[]}。

          新的方案帶來怎樣的收益呢?
          1. 增加自增mapid字段,一個群聊維護一份,成本幾乎可以忽略不計
          2. 每個成員已讀未讀由8B(64bit)優(yōu)化成2bit,減少62/64, 200人已讀未讀舊的方案1600B, 現(xiàn)在只需要(200/8) * 2 + 4 = 54 , 每條消息節(jié)約95%+

          如果maxid如果到百萬甚至千萬級別,那豈不是災難?一般實際場景,群聊是會限制人數(shù)的,就算不斷踢人加新人,那maxid最多也只能到企業(yè)人數(shù)。如果maxid達到一個特別大數(shù)字,已讀未讀對應的存儲可以增加多一個flag,如果bitmap存儲成本遠超過最初的方案,可以用最初的方案來實現(xiàn),客戶端提前埋好兼容邏輯就可以了。

          作者:小袁學習筆記

          來源:www.toutiao.com/i6686735232772604429/


          最后整理好了《Java大廠面試題》分類?16?PDF累計 1001頁!

          整理的面試題,內容列表


          互聯(lián)網(wǎng)大廠面試題,怎么領取?
          ?注意,不要亂回復?
          (一定要回復?面試題?)否則獲取不了

          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  夜色色综合 | 婷婷久久综合激情综合 | 无码自拍偷拍 | 青青草91在线视频 | 亚州在线观看视频 |