现代IM系统中聊天消息的同步和存储方案探讨

📅 2026/7/2 4:37:11
现代IM系统中聊天消息的同步和存储方案探讨
1、前言IM全称是“Instant Messaging”中文名是即时通讯。在这个高度信息化的移动互联网时代生活中IM类产品已经成为必备品比较有名的如钉钉、微信、QQ等以IM为核心功能的产品。当然目前微信已经成长为一个生态型产品但其核心功能还是IM。还有一些非以IM系统为核心的应用最典型的如一些在线游戏、社交应用IM也是其重要的功能模块。可以说带有社交属性的应用IM功能一定是必不可少的。IM系统在互联网初期即存在其基础技术架构在这十几年的发展中更新迭代多次从早期的CS、P2P架构到现在后台已经演变为一个复杂的分布式系统涉及移动端、网络、安全和存储等技术的方方面面。其支撑的规模也从早期的少量日活到现在微信这个巨头最新公布的达到9亿的日活的体量。IM系统中最核心的是消息系统消息系统最核心的是消息的同步和存储1消息的同步将消息完整的、快速的从发送方传递到接收方就是消息的同步。消息同步系统最重要的衡量指标就是消息传递的实时性、完整性以及能支撑的消息规模。从功能上来说一般至少要支持在线和离线推送高级的IM系统还支持“多端同步”2消息的存储消息存储即消息的持久化保存这里不是指消息在客户端本地的保存而是指云端的保存功能上对应的就是“消息漫游”。“消息漫游”的好处是可以实现账号在任意端登陆查看所有历史消息这也是高级IM系统特有的功能之一。本文内容主要涉及IM系统中的消息系统架构探讨一种适用于大用户量的消息同步以及存储系统的架构实现能够支持消息系统中的高级特性“多端同步”以及“消息漫游”。在性能和规模上能够做到全量消息云端存储百万TPS以及毫秒级延迟的消息同步能力。2、架构设计本章主要会介绍基于TableStore的现代IM消息系统的架构设计在详细介绍架构设计之前会先介绍一种Timeline逻辑模型来抽象和简化对IM消息同步和存储模型的理解。理解了Timeline模型后会介绍如何基于此模型对消息的同步以及存储进行建模。基于Timeline模型在实现消息同步和存储时还会有各方面的技术权衡例如如何对消息同步常见的读扩散和写扩散两种模型进行对比和选择以及针对Timeline模型的特征如何来选择底层数据库。▲ 上图是消息系统传统架构与现代架构的简单对比传统架构下消息是先同步后存储对于在线的用户消息会直接实时同步到在线的接收方消息同步成功后并不会进行持久化。而对于离线的用户或者消息无法实时同步成功时消息会持久化到离线库当接收方重新连接后会从离线库拉取所有未读消息。当离线库中的消息成功同步到接收方后消息会从离线库中删除。传统的消息系统服务端的主要工作是维护发送方和接收方的连接状态并提供在线消息同步和离线消息缓存的能力保证消息一定能够从发送方传递到接收方。服务端不会对消息进行持久化所以也无法支持消息漫游。现代架构下消息是先存储后同步先存储后同步的好处是如果接收方确认接收到了消息那这条消息一定是已经在云端保存了。并且消息会有两个库来保存一个是消息存储库用于全量保存所有会话的消息主要用于支持消息漫游。另一个是消息同步库主要用于接收方的多端同步。消息从发送方发出后经过服务端转发服务端会先将消息保存到消息存储库后保存到消息同步库。完成消息的持久化保存后对于在线的接收方会直接选择在线推送。但在线推送并不是一个必须路径只是一个更优的消息传递路径。对于在线推送失败或者离线的接收方会有另外一个统一的消息同步方式。接收方会主动的向服务端拉取所有未同步消息但接收方何时来同步以及会在哪些端来同步消息对服务端来说是未知的所以要求服务端必须保存所有需要同步到接收方的消息这是消息同步库的主要作用。对于新的同步设备会有消息漫游的需求这是消息存储库的主要作用在消息存储库中可以拉取任意会话的全量历史消息。以上是传统架构和现代架构的一个简单的对比现代架构上整个消息的同步和存储流程并没有变复杂太多但是其能实现多端同步以及消息漫游。现代架构中最核心的就是两个消息库“消息同步库”和“消息存储库”是消息同步和存储最核心的基础。而本篇文章接下来的部分都是围绕这两个库的设计和实现来展开。3、Timeline模型在分析“消息同步库”和“消息存储库”的设计和实现之前在本章会先介绍一个逻辑模型-Timeline。Timeline模型会帮助我们简化对消息同步和存储模型的理解而消息库的设计和实现也是围绕Timeline的特性和需求来展开。▲ Timeline模型如图是Timeline模型的一个抽象表述Timeline可以简单理解为是一个消息队列但这个消息队列有如下特性1每个消息拥有一个顺序IDSeqId在队列后面的消息的SeqId一定比前面的消息的SeqId大也就是保证SeqId一定是增长的但是不要求严格递增2新的消息永远在尾部添加保证新的消息的SeqId永远比已经存在队列中的消息都大3可根据SeqId随机定位到具体的某条消息进行读取也可以任意读取某个给定范围内的所有消息。有了这些特性后消息的同步可以拿Timeline来很简单的实现。图中的例子中消息发送方是A消息接收方是B同时B存在多个接收端分别是B1、B2和B3。A向B发送消息消息需要同步到B的多个端待同步的消息通过一个Timeline来进行交换。A向B发送的所有消息都会保存在这个Timeline中B的每个接收端都是独立的从这个Timeline中拉取消息。每个接收端同步完毕后都会在本地记录下最新同步到的消息的SeqId即最新的一个位点作为下次消息同步的起始位点。服务端不会保存各个端的同步状态各个端均可以在任意时间从任意点开始拉取消息。消息漫游也是基于Timeline和消息同步唯一的区别是消息漫游要求服务端能够对Timeline内的所有数据进行持久化。基于Timeline从逻辑模型上能够很简单的理解在服务端如何去实现消息同步和存储并支持多端同步和消息漫游这些高级功能。落地到实现的难点主要在如何将逻辑模型映射到物理模型Timeline的实现对数据库会有哪些要求我们应该选择何种数据库去实现这些是接下来会讨论到的问题。4、消息存储模型▲ 基于Timeline的消息存储模型如图是基于Timeline的消息存储模型消息存储要求每个会话都对应一个独立的Timeline。如图例子所示A与B/C/D/E/F均发生了会话每个会话对应一个独立的Timeline每个Timeline内存有这个会话中的所有消息服务端会对每个Timeline进行持久化。服务端能够对所有会话Timeline中的全量消息进行持久化也就拥有了消息漫游的能力。5、消息同步模型消息同步模型会比消息存储模型稍复杂一些消息的同步一般有读扩散和写扩散两种不同的方式分别对应不同的Timeline物理模型。▲ 读扩散和写扩散两种不同同步模式下对应的不同的Timeline模型如图是读扩散和写扩散两种不同同步模式下对应的不同的Timeline模型按图中的示例A作为消息接收者其与B/C/D/E/F发生了会话每个会话中的新的消息都需要同步到A的某个端看下读扩散和写扩散两种模式下消息如何做同步。读扩散消息存储模型中每个会话的Timeline中保存了这个会话的全量消息。读扩散的消息同步模式下每个会话中产生的新的消息只需要写一次到其用于存储的Timeline中接收端从这个Timeline中拉取新的消息。优点是消息只需要写一次相比写扩散的模式能够大大降低消息写入次数特别是在群消息这种场景下。但其缺点也比较明显接收端去同步消息的逻辑会相对复杂和低效。接收端需要对每个会话都拉取一次才能获取全部消息读被大大的放大并且会产生很多无效的读因为并不是每个会话都会有新消息产生。写扩散写扩散的消息同步模式需要有一个额外的Timeline来专门用于消息同步通常是每个接收端都会拥有一个独立的同步Timeline用于存放需要向这个接收端同步的所有消息。每个会话中的消息会产生多次写除了写入用于消息存储的会话Timeline还需要写入需要同步到的接收端的同步Timeline。在个人与个人的会话中消息会被额外写两次除了写入这个会话的存储Timeline还需要写入参与这个会话的两个接收者的同步Timeline。而在群这个场景下写入会被更加的放大如果这个群拥有N个参与者那每条消息都需要额外的写N次。写扩散同步模式的优点是在接收端消息同步逻辑会非常简单只需要从其同步Timeline中读取一次即可大大降低了消息同步所需的读的压力。其缺点就是消息写入会被放大特别是针对群这种场景。在IM这种应用场景下通常会选择写扩散这种消息同步模式。IM场景下一条消息只会产生一次但是会被读取多次是典型的读多写少的场景消息的读写比例大概是10:1。若使用读扩散同步模式整个系统的读写比例会被放大到100:1。一个优化的好的系统必须从设计上去平衡这种读写压力避免读或写任意一维触碰到天花板。所以IM系统这类场景下通常会应用写扩散这种同步模式来平衡读和写将100:1的读写比例平衡到30:30。当然写扩散这种同步模式还需要处理一些极端场景例如万人大群。针对这种极端写扩散的场景会退化到使用读扩散。一个简单的IM系统通常会在产品层面限制这种大群的存在而对于一个高级的IM系统会采用读写扩散混合的同步模式来满足这类产品的需求。采用混合模式会根据数据的不同类型和不同的读写负载来决定用写扩散还是读扩散。6、典型架构设计如上图所示是一个典型的消息系统架构。该典型的消息系统架构中包含几个重要组件1端作为消息的发送和接收端通过连接消息服务器来发送和接收消息。2消息服务器一组无状态的服务器可水平扩展处理消息的发送和接收请求连接后端消息系统。3消息队列新写入消息的缓冲队列消息系统的前置消息存储用于削峰填谷以及异步消费。4消息处理一组无状态的消费处理服务器用于异步消费消息队列中的消息数据处理消息的持久化和写扩散同步。5消息存储和索引库持久化存储消息每个会话对应一个 Timeline 进行消息存储存储的消息建立索引来实现消息检索。6消息同步库写扩散形式同步消息每个用户的收件箱对应一个 Timeline同步库内消息不需要永久保存通常对消息设定一个生命周期。新消息会由端发出通常消息体中会携带消息 ID用于去重、逻辑时间戳用于排序、消息类型控制消息、图片消息或者文本消息等、消息体等内容。消息会先写入消息队列作为底层存储的一个临时缓冲区。消息队列中的消息会由消息处理服务器消费可以允许乱序消费。消息处理服务器对消息先存储后同步先写入发件箱 Timeline存储库后写扩散至各个接收端的收件箱同步库。消息数据写入存储库后会被近实时的构建索引索引包括文本消息的全文索引以及多字段索引发送方、消息类型等。对于在线的设备可以由消息服务器主动推送至在线设备端。对于离线设备登录后会主动向服务端同步消息。每个设备会在本地保留有最新一条消息的顺序 ID向服务端同步该顺序 ID 后的所有消息。7、消息库设计基于Timeline模型以及Timeline模型在消息存储和消息同步的应用我们看下消息同步库和消息存储库的设计。▲ 基于Timeline的消息库设计消息同步库消息同步库用于存储所有用于消息同步的Timeline每个Timeline对应一个接收端主要用作写扩散模式的消息同步。这个库不需要永久保留所有需要同步的消息因为消息在同步到所有端后其生命周期就可以结束就可以被回收。但是如前面所介绍的一个实现简单的多端同步消息系统在服务端不会保存有所有端的同步状态而是依赖端自己主动来做同步。所以服务端不知道消息何时可以回收通常的做法是为这个库里的消息设定一个固定的生命周期例如一周或者一个月生命周期结束可被淘汰。消息存储库消息存储库用于存储所有会话的Timeline每个Timeline包含了一个会话中的所有消息。这个库主要用于消息漫游时拉取某个会话的所有历史消息也用于读扩散模式的消息同步。消息同步库和消息存储库对数据库有不同的要求如何对数据库做选型在下面会讨论。8、数据库选型消息系统最核心的两个库是消息同步库和消息存储库两个库对数据库有不同的要求总结下来对数据库的要求有如下几点1表结构设计能够满足Timeline模型的功能要求不要求关系模型能够实现队列模型并能够支持生成自增的SeqId2能够支持高并发写和范围读规模在十万级TPS3能够保存海量数据百TB级4能够为数据定义生命周期。9、本文小结