做IM也有1年多了,之前的群聊消息一直采用的是在一张群消息表里存储,表结构如下:

字段 类型 描述
id int 主键
msg varchar 文本
userid int 发送方ID
sendtime int 发送方时间
roomid int 群ID

    为了做消息确认(保证每个人都能收到消息),目前才用的是,读取用户最后下线时间后的所有群聊消息,姑且称作策略1。这样做基本能够读取到所有的消息,但是不安全,既不能保证每条消息都能收到,什么情况呢,下面做一下分析:

    1.A用户正在下线,B用户在群里面说了一句话,这时候A不一定收到群消息了,即使收到也不能再阅读了,因为已经做了下线操作。

    2.A用户收到B用户在群里说的一句话,但是还没有看的时候程序异常关闭了,这条消息也没有正真读取。

    3.A用户客户端进程意外退出,这时候并未向服务器发送下线通知,服务器只能通过心跳维持到时后才能知道A用户下线了,然后做下线记录,但是这期间(A僵死了)的群聊消息在A上线后也不能收到了(因为离线群聊消息是读取用户最后下线时间后的所有消息

   为了解决策略1的缺点,经过一番思考,于是又了策略2:群消息分发给所有的用户,并拷贝N份(群用户数目)做保存,这里扩展了一张表,结构如下:

字段 类型 描述
id int 主键
userid int 接收方用户ID
senduserid int     发送方用户ID
msg varchar 文本
status int 状态
    .     .     .

    采用这样的表结构来存储后,每个用户单独对应一条群聊天消息的拷贝,这样就方便单独管理消息读取状态,用了一段时候后,发现这张表的数据激增。

    为了解决策略2的缺点,结果几番思考,产生了策略3:群消息分发给在线用户,在线用户收到群消息后回执状态给服务器,服务器做一个消息关联表,结构如下:

字段 类型 描述
id int 主键
roomsgid int 外键,群消息ID
status int           状态
    首先群消息发送出去后不再拷贝N份存储了,然后存储也只存储对应关系,还有是收到群消息的才存储,对哪些僵尸用户就不存储了,这样省掉了很多不必要的数据拷贝存储。虽然这种策略目前已经比较完美,但是还有数据量大的隐患存在。

    为了解决策略3的问题,结果N番思考,产生了策略4:设计一张表用来保存用户每个群里最后读取到的消息时间,下次读取这个时间以后的群消息,表结构如下:

字段 类型 描述
id int 主键
roomid int    群ID
userid int 用户iD
lastrtime int         最后接收时间
这样需要记录的信息就少多了,因为每次收到群消息后,只需要提交最后一条消息的时间给服务器,服务器更新记录即可

后记:策略4的优化,将lastrtime字段设计到群成员表即可,也不需要再建立一张表了。