一、前言
站内信”不同于电子邮件和实时通讯,电子邮件通过专门的邮件服务器发送、保存;实时系统的通讯像QQ那样建立长连接实现,消息一般存储在客户端。而“站内信”是系统内的消息,说白了,“站内信”的实现,就是通过数据库插入记录来实现的.
二、总体描述.
1、“站内信”有两个基本功能。
(1)点到点的消息传送。用户给用户发送站内信;管理员给用户发送站内信。
(2)点到面的消息传送。管理员给用户(指定满足某一条件的用户群)群发消息
2、功能细分
(1)关于点对点的消息传输比较明确,就是我们和他人的私聊,很容易实现
(2)关于点到面的消息传送,在具体功能上还是可以细化一下的
(a)强制广播的发送,类似于现在各大视频app中打开时弹出的的“青少年模式”,该模式也可以分成给全员发送(例如公告),以及部分发送(例如部分人员通知开会)的两种方式。该方式是强制接受的广播
(b)关注人员的的推送,类似于微信公众号的关注后的消息推送,即该方式是接受者自愿接受的广播
三、数据库表设计
1、第一种情况,站内的用户是少量级别的。
这种情况,由于用户的数量非常少,因此,没有必要过多的考虑数据库的优化,采用简单的表格,对系统的设计也来的简单,后期也比较容易维护,是典型的用空间换时间的做法。
数据库的设计如下:表名:Message
ID:编号;SendID:发送者编号;RecID:接受者编号(如为0,则接受者为所有人);Message:站内信内容;Statue:站内信的查看状态;PDate:站内信发送时间;
如果,某一个管理员要给所有人发站内信,则先遍历用户表,再按照用户表中的所有用户依次将站内信插入到Message表中。这样,如果有个用户,则群发一条站内信要执行n个插入操作。这个理解上比较简单,比较耗损空间。
2、第二种情况,站内的用户中量级别的。
如果还是按照第一种情况的思路。那发一条站内信的后果基本上就是后台崩溃了。因为,发一条站内信,得重复上千个插入记录,这还不是最主要的,关键是上千乃至上万条记录,Message字段的内容是一样的,而Message有大量的占用存储空间。因此,将原先的表格拆分为两个表,将Message的主体放在一个表内,节省空间的占用。这样的设计,将重复的站内信的主体信息(站内信的内容,发送时间)放在一个表内,大量的节省存储空间。不过,在查询的时候,要比第一种情况来的复杂。
表名:notify
ID:编号;messageType:消息类型;messageContentID:消息体ID;SendID:发送者编号;RecID:接受者编号;Statue:站内信的查看状态;
表名:message_content
ID:编号;Message:站内信的内容;PDate:站内信发送时间;
3、站内的用户是较大量级的
当注册了一个用户。过了一段时间,由于种种原因,就不再登陆了。那么这个用户就称为不活跃的。从实际来看,不活跃的用户占着不小的比例。我们以注册用户2百万,其中活跃用户只占其中的10%。
那么,用户在登录以后,首先查询message_content中的那些没有在notify中有记录的记录,表示是未读的站内信。在查阅站内信的内容时,再将相关的记录插入到notify中。
四、实现
1、该文采用第二种数据库表的设计方式实现站内信的设计,能够基本完成我们的需求,也有不完美的地方,例如当发送公告时,按照上面的系统就直接暴力给每个用户都加一条通知消息;或者通过第三种方式通过反查message_content中的那些没有在notify中有记录的记录
-- 用户关注表,记录了所有用户的关注信息
CREATE TABLE `m_user_follow`
(
`follow_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`uid` varchar(20) NOT NULL COMMENT '用户ID',
`follow_uid` varchar(20) NOT NULL COMMENT '关注的用户id',
PRIMARY KEY (`follow_id`),
UNIQUE INDEX(`uid` ,`follow_uid`)
) COMMENT ='用户关注表,记录了所有用户的关注信息';
-- 用户通知表
CREATE TABLE `m_notify`
(
`notify_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '通知主键',
`message_type` char(2) NOT NULL COMMENT '消息类型:01-广播,02-关注消息,03-私信',
`sender_id` varchar(20) NOT NULL COMMENT '发送者用户ID',
`receiver_id` varchar(32) COMMENT '接受者用户ID',
`message_status` char(2) NOT NULL DEFAULT '01' COMMENT '消息状态:01-未读,12-已读,03-删除',
`content_id` varchar(24) NOT NULL COMMENT '对应内容的id',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`notify_id`)
) COMMENT ='用户通知表';
-- 站内信息内容表
CREATE TABLE `m_message_content`
(
`content_id` varchar(32) NOT NULL COMMENT '内容主键',
`title` varchar(24) NOT NULL COMMENT '消息标题',
`content` varchar(1000) NOT NULL COMMENT '消息内容,最长长度不允许超过1000',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`content_id`)
) COMMENT ='站内信息内容表';
五、其他
1、上面的设计是基于单体项目进行实现的,当涉及到数据库分库分表的情况下就不特别合适了,当用户基数足够庞大是,可以通过对上边的不同类型的站内信单独建notify表,即私聊、广播、关注,而且可以实现使一些冗余数据不再保存。数据是单表查询,关联处理最好是在程序里面做join,merge。
2、在消息的存储,可以考虑将经常访问公告的信息备份到redis中,设置key一定的有效时间段。