站内信的数据库设计

站内信现在几乎可以说是任何一套web系统的标配模块,那怎么样设计一个优雅的站内信模块呢。

首先,我们将站内信区分一下:

  1. 点到点:一对一发送的站内信,比如用户A发给用户B的消息,或者系统发给用户A的通知。type=private
  2. 点到多:一对多发送的站内信,接收对象较少,而且接收对象无特殊共性。可以当做私信。type=private
  3. 点到面:一对多发送的站内信,接收对象较多,而且接收对象同属于某用户组之类的共同属性。type=group
  4. 点到全部:一对多发送的站内信,接收对象为全部用户。通常为系统通知。type=global

一、用户量少

可以采用最简单的实现方式,只需要用到一张message表

屏幕快照 2017-11-09 下午5.26.05

不管是一对一的私信还是一对多的群发,每发给一个用户,就在表中插入一条数据,简单粗暴。

但是如果考虑到一对多的消息比较多的时候,消息的content字段其实是冗余的,可以将这张表拆成两份

二、拆分消息表

message表

屏幕快照 2017-11-09 下午5.39.26

message_content表

屏幕快照 2017-11-09 下午5.51.19

这样拆分后,对于一对一的私信,需要在message表与message_content表各插入一条数据。

对于一对多的消息,在message_content表只需要插入一条数据,大大节省了消息数据的存储空间。不过每发给一个用户,仍需要对应的在message表中插入一条数据。

再考虑一下,如果用户数量已经增长到百万级别,那么这个时候想要给所有用户发送一条全局系统通知,虽然说在message_content中只需要插入一条数据(type=global, sender_id=0),但message表中却仍要插入百万条数据,这个数据量是很可怕的。

三、用户量大

既然我们不希望因为一条系统广播而为每一个用户都在message表中插入一条数据。那么还有什么办法可以减少message表的存储量呢。不妨逆向思维一下,对于这种广播类的消息(type=group||global),并不是为每一个接收用户在message表中插入一条数据,而是只有当用户处理过该消息后(status=已读||删除)才将这一行插入message表。根据28原则,一个网站的活跃用户大概也就20%甚至更少。这样,只在message表中存储已处理的广播消息,能极大的减少存储空间。

所以,message与message_content表结构都不用变。

对于type=private类的消息,在message_content表写入该消息,然后对每一位接收用户写入一行数据在message表。

对于type=group||global类的消息,在message_content表写入该消息,然后只有当接收用户处理过该消息后,才写入一条status=已读||删除的记录到message表。

为了方便查询,可以在上面的message表结构中增加一列冗余的type字段。另外,group类型的广播消息类似于global全局广播消息,我上面的表结构设计为了简单没有涉及group_id字段,所以下面例子不做赘述。

要查询用户A的未读私信:

select meesage_id from message where receiver_id=A and status=’未读’;

要查询用户A的已读私信:

select message_id from message where receiver_id=A and type=’private’ and status=’已读’;

要查询用户A的未读广播消息:

select id from message_content where type=’global’ and id not in (select message_id from message where receiver_id=A);

要查询用户A的已读广播消息:

select message_id from message where receiver_id=A and type=’global’ and status=’已读’;

参考:

http://www.cnblogs.com/hejiaquan/archive/2012/04/07/2435817.html

 

 

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top