站内信现在几乎可以说是任何一套web系统的标配模块,那怎么样设计一个优雅的站内信模块呢。
首先,我们将站内信区分一下:
- 点到点:一对一发送的站内信,比如用户A发给用户B的消息,或者系统发给用户A的通知。type=private
- 点到多:一对多发送的站内信,接收对象较少,而且接收对象无特殊共性。可以当做私信。type=private
- 点到面:一对多发送的站内信,接收对象较多,而且接收对象同属于某用户组之类的共同属性。type=group
- 点到全部:一对多发送的站内信,接收对象为全部用户。通常为系统通知。type=global
一、用户量少
可以采用最简单的实现方式,只需要用到一张message表
不管是一对一的私信还是一对多的群发,每发给一个用户,就在表中插入一条数据,简单粗暴。
但是如果考虑到一对多的消息比较多的时候,消息的content字段其实是冗余的,可以将这张表拆成两份
二、拆分消息表
message表
message_content表
这样拆分后,对于一对一的私信,需要在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