兩年前,萬倉一黍在發了兩篇關於站內信的設計實現博文,《**「站內信」的實現》、《**「站內信」的實現(續)》,其中闡述了他關於站內信**的設計思想,很具有借鑑意義。他在設計時考慮到使用者量和儲存空間的占用等問題。當然,在他的兩篇博文中強調了站內信的設計要考慮具體情況,沒有理想的設計方案,他的設計只是對於**(點到面)的解決方案。 在此簡述一下他的設計方案,詳細的可以移步萬倉一黍的部落格。
萬倉一黍的設計方案:
站內信分為「點到點」和「點到面」,「點到點」屬於私信,使用者之間傳遞的資訊,一對一傳遞。「點到面」,屬於系統訊息或者公共資訊,屬於一對多傳送。
站內信的設計既要考慮到投遞的準確性(也就是該收到的人能收到資訊),也要考慮資訊持久化儲存空間占用問題,在他的第一篇博文中詳細進行了介紹。
我們在此僅把第三種情況拿出來說明,也就是使用者量為百萬級,活躍使用者只佔其中的一部分。
資料庫的設計:將一封message分為兩部分,一是儲存內容,另乙個是儲存使用者的檢視狀態。也就解決了關於**資訊的儲存空間占用問題,不需要為每個使用者插入相關資料。表名:message
id:編號;sendid:傳送者編號;recid:接受者編號(如為0,則接受者為所有人);messageid:站內信編號;statue:站內信的檢視狀態;
表名:messagetext
id:編號;message:站內信的內容;pdate:站內信傳送時間;
另外考慮到百萬級使用者量,活躍使用者只佔其中的一部分,不可能在傳送一封系統訊息時,在message表中為每乙個使用者插入一條狀態(標記為未讀),如果一百萬使用者,那麼傳送一條訊息,就得往message表中插入一百萬條標記狀態的資料,顯然不具有可行性。所以萬倉一黍提出換下思路:
在使用者登入時檢索message和messgaetext,將messgaetext的id 和messgae 的messageid 相匹配, 這樣就有兩種情況:
一、沒有找到 recid= 自己id 並且messagetext中的訊息id不包含在messgae的messageid中
二、找到recid=自己 並且 messagetext中的訊息id包含在messgae的messageid中,status標記為已讀
將此部分訊息提前出來,顯示為使用者已讀,如果想「刪除」(當然是邏輯上的刪除,並非物理資料庫刪除),設定該status=刪除。
對於上面的設計方案,設計系統訊息**全部使用者,是很適合的。但是受眾面越小(即「點到面」的面越小),就不太合適,所以我們需要在此設計方案上進行擴充套件。
只對上面提到的使用者百萬級且活躍使用者只佔一部分這種情況**。還是採用將訊息內容和閱讀狀態分開設計。
我們將點到點和點到面綜合到一起考慮,並且精細化這個「面」,不再是籠統的全部使用者。「面」可以是具有某一角色的使用者、某一使用者組的使用者甚至一些不具有任何公共特徵的雜湊使用者。
概述如下:我們將訊息分為私信(private)、公共訊息(public)、系統訊息(global)(或者將公共訊息和系統訊息合併為公共訊息也可以),視情況而定。
點到點:一對一傳送,屬於私信private
點到個別:(接收面為百位使用者)一對多(幾百)傳送,採用私信方式(private)
點到區域性:(接收面為具有某些公共特徵如使用者組、使用者角色),屬於公共訊息(public)
點到全部:一對全部傳送,屬於系統訊息(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=刪除
採用和私信相同的方式,在傳送一條訊息時在messagetext表中插入訊息內容並且設定type=private,同時在message表中插入多條記錄設定recid=各接收者id,status=未讀
每個接收採用和私信一樣的方式讀取處理。
點到區域性是一對某角色或某使用者組傳送,例如管理員向普通使用者組傳送,在messagetext表插入訊息內容,且設定type=public 和group為使用者組id
使用者登入後分兩種情況:
1、未找到recid=自己id 且 messagetext中(type=public 和group=自己所在組 ) 的訊息id不包含在messgae的messageid中
2、找到recid=自己id 且 messagetext中(type=public 和group=自己所在組 ) 的訊息id包含在messgae的messageid中
將此部分訊息提取出來,顯示為使用者公共訊息已讀,如果想「刪除」(當然是邏輯上的刪除,並非物理資料庫刪除),設定該status=刪除。
注:此時可以不驗證group=自己所在組
點到全部和點到區域性採用類似的處理方式。例如管理員向普通使用者組傳送,在messagetext表插入訊息內容,且設定type=global
使用者登入後分兩種情況:
1、未找到recid=自己id 且 messagetext中(type=global ) 的訊息id不包含在messgae的messageid中
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中 ,顯示為系統訊息已讀
畢業兩年後的反思
轉眼就畢業兩年了,在這兩年裡,幾乎是在上班,加班,中度過的。為了自己在學校的理想,一直努力奮鬥著。這兩年裡,汗水,心酸,坎坷總是耐人回味。這些算是在自己奮鬥道路上的見證或是對其短暫的留戀吧 遙想當年,為了自己的專業,滿腔熱血,身居校處,出沒於機房,每天上課都是滿腦子的為什麼,有時候,把老師問的直說,...
寫給兩年後的自己
之前曾在這裡看到一篇文章 寫給畢業三年後的自己 有所感觸,所以我也想寫一篇。也算是自己在這裡寫下的第一篇文章吧。我現在21歲,之前在社會上有兩年 工作經歷的我,有選擇來學校學習 起初我是對自己非常沒用信心,覺得自己現在都這個年齡了還能學的進去嗎。各位不要笑話我,在我家那裡 向我這個年齡的人都有結婚的...
群發「站內信」的實現
在很多 系統 如cms系統,sns系統等 都有 站內信 的功能。站內信 不同於電子郵件,電子郵件通過專門的郵件伺服器傳送 儲存。而 站內信 是系統內的訊息,說白了,站內信 的實現,就是通過資料庫插入記錄來實現的。站內信 有兩個基本功能。一 點到點的訊息傳送。使用者給使用者傳送站內信 管理員給使用者傳...