站內信DB設計實現

[轉載]:點擊打開鏈接


兩年前,萬倉一黍在博客園發了兩篇關於站內信的設計實現博文,《羣發“站內信”的實現》、《羣發“站內信”的實現(續)》,其中闡述了他關於站內信羣發的設計思想,很具有借鑑意義。他在設計時考慮到用戶量和存儲空間的佔用等問題。當然,在他的兩篇博文中強調了站內信的設計要考慮具體情況,沒有理想的設計方案,他的設計只是對於羣發(點到面)的解決方案。 在此簡述一下他的設計方案,詳細的可以移步萬倉一黍的博客。

萬倉一黍的設計方案:

站內信分爲“點到點”和“點到面”,“點到點”屬於私信,用戶之間傳遞的信息,一對一傳遞。“點到面”,屬於系統消息或者公共信息,屬於一對多發送。

站內信的設計既要考慮到投遞的準確性(也就是該收到的人能收到信息),也要考慮信息持久化存儲空間佔用問題,在他的第一篇博文中詳細進行了介紹。

我們在此僅把第三種情況拿出來說明,也就是用戶量爲百萬級,活躍用戶只佔其中的一部分。

數據庫的設計:

表名:Message

ID:編號;SendID:發送者編號;RecID:接受者編號(如爲0,則接受者爲所有人);MessageID:站內信編號;Statue:站內信的查看狀態;

表名:MessageText

ID:編號;Message:站內信的內容;PDate:站內信發送時間;

將一封Message分爲兩部分,一是存儲內容,另一個是存儲用戶的查看狀態。也就解決了關於羣發信息的存儲空間佔用問題,不需要爲每個用戶插入相關數據。

另外考慮到百萬級用戶量,活躍用戶只佔其中的一部分,不可能在發送一封系統消息時,在Message表中爲每一個用戶插入一條狀態(標記爲未讀),如果一百萬用戶,那麼發送一條消息,就得往Message表中插入一百萬條標記狀態的數據,顯然不具有可行性。所以萬倉一黍提出換下思路:

在用戶登錄時檢索Message和MessgaeText,將MessgaeText的ID 和Messgae 的MessageID 相匹配, 這樣就有兩種情況:

一、沒有找到  RecID= 自己ID 並且MessageText中的消息ID不包含在Messgae的MessageID中

       將此部分消息取出來,顯示爲該用戶未讀,在用戶點擊閱讀的時候,將消息閱讀狀態寫入Messgae表,Status=已讀。

二、找到RecID=自己 並且 MessageText中的消息ID包含在Messgae的MessageID中,Status標記爲已讀

       將此部分消息提前出來,顯示爲用戶已讀,如果想“刪除”(當然是邏輯上的刪除,並非物理數據庫刪除),設置該Status=刪除。

 

對於上面的設計方案,設計系統消息羣發全部用戶,是很適合的。但是受衆面越小(即“點到面”的面越小),就不太合適,所以我們需要在此設計方案上進行擴展。

只對上面提到的用戶百萬級且活躍用戶只佔一部分這種情況探討。還是採用將消息內容和閱讀狀態分開設計。

我們將點到點和點到面綜合到一起考慮,並且精細化這個“面”,不再是籠統的全部用戶。“面”可以是具有某一角色的用戶、某一用戶組的用戶甚至一些不具有任何公共特徵的散列用戶。

設計思路

概述如下:我們將消息分爲私信(Private)、公共消息(Public)、系統消息(Global)(或者將公共消息和系統消息合併爲公共消息也可以),視情況而定。

  1. 點到點:一對一發送,屬於私信Private
  2. 點到個別:(接收面爲百位用戶)一對多(幾百)發送,採用私信方式(Private)
  3. 點到局部:(接收面爲具有某些公共特徵如用戶組、用戶角色),屬於公共消息(Public)
  4. 點到全部:一對全部發送,屬於系統消息(Global)

數據庫設計

表名:Message

ID:編號;RecID:接收者編號;MessageID:站內信編號;Statue:站內信的查看狀態

表名:MessageText

ID:編號;SendID:發送者編號;Message:站內信的內容;Type:信息類型;Group:用戶組ID;  PostDate:站內信發送時間

其中Status狀態有未讀、已讀、刪除

        Type類型有Private(私信)、Public(公共消息)、Global(系統消息)

第一種 點到點

  點到點發送屬於私信,比如A用戶發送給B用戶,首先在MessageText表中插入消息內容並且設置Type=Private,同時在Message表中插入一條記錄設置RecID=B,Status=未讀

  用戶B查找RecID=B,並且Staus爲未讀,Type=Private,顯示爲私信未讀,點擊閱讀後改變Status=已讀

  用戶B查找RecID=B,並且Staus爲已讀,Type=Private,顯示爲私信已讀,刪除設置Status=刪除

第二種 點到個別

  採用和私信相同的方式,在發送一條消息時在MessageText表中插入消息內容並且設置Type=Private,同時在Message表中插入多條記錄設置RecID=各接收者ID,Status=未讀

  每個接收採用和私信一樣的方式讀取處理。

第三種 點到局部

   點到局部是一對某角色或某用戶組發送,例如管理員向普通用戶組發送,在MessageText表插入消息內容,且設置Type=Public 和Group爲用戶組ID

   用戶登錄後分兩種情況:

  1、未找到RecId=自己ID 且 MessageText中(Type=Public 和Group=自己所在組 )  的消息ID不包含在Messgae的MessageID中

       提取出來顯示爲用戶公共消息未讀,在用戶點擊閱讀的時候,將消息閱讀狀態寫入Messgae表,Status=已讀。

  2、找到RecId=自己ID 且 MessageText中(Type=Public 和Group=自己所在組 ) 的消息ID包含在Messgae的MessageID中

      將此部分消息提取出來,顯示爲用戶公共消息已讀,如果想“刪除”(當然是邏輯上的刪除,並非物理數據庫刪除),設置該Status=刪除。

      注:此時可以不驗證Group=自己所在組

第四種 點到全部

    點到全部和點到局部採用類似的處理方式。例如管理員向普通用戶組發送,在MessageText表插入消息內容,且設置Type=Global

    用戶登錄後分兩種情況:

    1、未找到RecId=自己ID 且 MessageText中(Type=Global ) 的消息ID不包含在Messgae的MessageID中

        提取出來顯示爲用戶系統消息未讀,在用戶點擊閱讀的時候,將消息閱讀狀態寫入Messgae表,Status=已讀。

    2、找到RecId=自己ID 且 MessageText中(Type=Global ) 的消息ID包含在Messgae的MessageID中

        將此部分消息提取出來,顯示爲用戶系統消息已讀,如果想“刪除”(邏輯上的刪除,並非物理數據庫刪除),設置該Status=刪除。

 

處理流程

我們再來看下整個處理流程,用戶登錄後系統是怎樣提取和顯示信息的。

用戶登錄後,採用Ajax異步加載、統計用戶站內信

  • Messgae表中RecId=自己ID 且Status=未讀,顯示爲私信未讀
  • Messgae表中RecId=自己ID 且Status=已讀 且 Type=Private,顯示爲私信已讀
  • Messgae表中未找到RecId=自己ID 且 MessageText中(Type=Public 和Group=自己所在組 ) 的消息ID不包含在Messgae的MessageID中,顯示爲公共消息未讀            
  • Messgae表中找到RecId=自己ID 且 MessageText中(Type=Public ) 的消息ID包含在Messgae的MessageID中 ,顯示爲公共消息已讀
  • Messgae表中未找到RecId=自己ID 且 MessageText中(Type=Global ) 的消息ID不包含在Messgae的MessageID中 ,顯示爲系統消息未讀
  • Messgae表中找到RecId=自己ID 且 MessageText中(Type=Global ) 的消息ID包含在Messgae的MessageID中 ,顯示爲系統消息已讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章