专栏首页普通程序员IM系统海量消息数据是怎么存储的?

IM系统海量消息数据是怎么存储的?

一、与消息相关的主要场景

1、存储和离线消息。

现在的IM系统,消息都要落地存储。这样如果接收消息的用户不在线,等他下次上线时,能获取到消息数据。

2、消息漫游

消息漫游包括主要两种场景,

(1)用户新安装IM软件,要能看到以前的聊天记录

(2)聊天软件有PC版和App版,在App上聊的天,打开PC版要能够看到

二、不同场景读取 消息关键点

1、拉取离线消息

每个用户打开App就需要拉取离线,网络中断重连后要拉取离线,收到消息序列号不连续也要拉取离线,拉取离线消息是一个高频操作 。离线消息包括单聊、群聊、控制类等消息,消息类型类型众多。因此离线消息需要以用户ID(多端情况下需要以端)为检索维度。说的直白一点,就是每个人(端)都需要一个收件箱,拉离线消息就是把个人(端)收件箱里的消息取到客户端。

2、消息漫游

消息漫游的典型使用场景是,打开某个会话(单聊、群聊、公众号),下拉界面,客户端向服务端请求这个会话的聊天数据。消息漫游需要以会话为检索维度。消息漫游拉取数据的频率相对较低。我们把这类获取消息的方式成为拉取历史消息。

三、存储消息关键点

1、离线消息

离线消息读取频繁(写也有一定压力),但是检索逻辑简单(参看《一个海量在线用户即时通讯系统(IM)的完整设计》拉取离线消息章节)。我们采用内存数据库(Redis)存储,主要结构使用SortedSet(可以有更高效的存储结构,但Redis不支持)。对于群消息,采用扩散写方式(一条群消息给每个群成员都写一份)。按照消息接受者ID水平分库。

2、历史消息

历史消息的访问频率低,但是每条消息都需要存储,我们采用关系型数据库(MySQL)存储,重点考虑写入效率。对于群消息,采用扩散读方式(每条群消息只写一条记录)。按照消息发送者ID(单聊),或群ID(群聊)进行水平分库。

四、消息存取方案

1、离线消息

存储离线消息。按照消息接收者ID(toID),取模Hash分库(也可以用一致性Hash)。每个用户创建一个SortedSet结构的Key,用于存储离线消息。离线消息按照时间先后顺序排列即可。SortedSet添加一个元素时间复杂度是O(log(N)),N 是有序集的基数,由于离线消息的msgid是有序的,所以实际插入时间复杂度很可能退化为O(1)。

读取离线消息。离线消息读取策略参看《一个海量在线用户即时通讯系统(IM)的完整设计》拉取离线消息章节。理论上读取离线消息的时间复杂度为O(log(N)+M), N 为离线消息的条数, M 为一次读取消息的条数。实际上,由于离线消息从有序集的头部开始读取,实际时间复杂度比这个值低。

2、历史消息

历史消息分为两大类,单聊消息、群聊消息。

单聊消息按照发送者ID(fromId)水平(取模Hash)分库,存到一张数据表(例如叫msg_user_send)中。核心字段包括msgid(消息ID),fromId(发送者Id),toId(接收者Id),content(消息内容)。

拉取单聊历史消息时(假设拉取userId1跟userId2的聊天),分别读取两人给对方发送的消息(因为分库原因,两人发送的消息可能分布在不同数据库中),然后进行Merge。

群聊消息按照群ID(groupId)水平(取模Hash)分库,存到一张表中(例如叫:msg_group)。核心字段包括msgid(消息ID),fromId(发送者Id),groupId(群Id),content(消息内容)。

另外一张msg_group_user表记录用户加入群的时间,如下图。某个人(如张三)加入群的时间,相当于一个游标,群消息表中,这个游标之后的聊天消息,是这个人(张三)能够查看的数据(当然,也可以做查看加入群之前若干条消息)。

拉取群历史消息,直接倒序读取这个群消息表数据即可。

由于MySQL和Redis都采用了水平分库,存储能力几乎可以线性扩展!是不是这样就足够了呢?答案是否定的,优化永远没有尽头。如果我在非洲某个国家登录系统,从北京的机房读取消息数据显然不太合适!如何让数据靠近用户,是一个更加有挑战的问题。

本文分享自微信公众号 - 普通程序员(farmerbrag),作者:封宇

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-08-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一个海量在线用户即时通讯系统(IM)的完整设计

    移动端重点是移动端,支持IOS/Android系统,包括IM App,嵌入消息功能的瓜子App,未来还可能接入客服系统。

    普通程序员
  • 一个海量在线用户即时通讯系统(IM)的完整设计Plus

    《一个海量在线用户即时通讯系统(IM)的完整设计》(以下称《完整设计》)这篇文章发出来之后有不少读者咨询问题,提出意见或建议。主要集中在模块拆分、协议、存储等方...

    普通程序员
  • 大规模群消息推送如何保证实时性?

    第一版红包功能上线后,收集到不少问题。核心问题是消息延迟,导致有些人先看到红包,有些人晚看到红包,同时导致消息顺序混乱。

    普通程序员
  • 【RAC】替换OCR磁盘组的步骤(视频+文档)

    http://blog.itpub.net/26736162/viewspace-2141215/

    小麦苗DBA宝典
  • 【业界】网站恶意挖矿脚本暴增725%?互联网安全公司表示这可能有你一份

    AiTechYun 编辑:xiaoshan 根据一项新的研究表明,在2017年底,托管加密货币挖矿脚本的域名数量增长了725%。这种对计算资源的消耗可能与恶意软...

    AiTechYun
  • 《Dark Reader》为任意网站启用夜间模式

    Dark Reader是一款Chrome护眼插件,可以实时生成黑色主题,为任意网站启用夜间模式

    zhaoolee
  • 【云安全】应用程序和架构设计在云计算环境下的安全建议

    应用程序可以轻松地在属于自己隔离的云环境中运行,根据提供者的不同,可能是一个单独的虚拟网络(虚拟专有网络VPC),也可能是一个一个单独的账号/子账号。使用账户或...

    一只特立独行的兔先生
  • tkinter -- Label之bitmap使用方法

    py3study
  • 图片加载利器——Picasso

    piacsso是Square公司开源的一个Android的图形缓存库 官网地址:http://square.github.io/picasso/ Images ...

    Demo_Yang
  • 【每日一题】问题 1225: 文科生的悲哀

    题目描述 化学不及格的Matrix67无奈选择了文科。他必须硬着头皮准备一次又一次的文科考试。 在这一学期一共有n次文科考试,考试科目有4种,分别为政治...

    编程范 源代码公司

扫码关注云+社区

领取腾讯云代金券