IM专题:模型分析(3)—读写扩散模型

读写扩散模型,即读扩散和写扩散模型;

读扩散,也叫拉取模型,描述对数据进行读取操作时,有更多的读取动作;

写扩散,也叫推送模型,描述对数据进行写入操作时,有更多的写入动作。

IM 系统的读写扩散模型主要体现在对群消息的处理上,在之前的文章中(IM专题:分层架构IM系统(14)—群消息逻辑实现 )曾经分析过群消息的处理方式,我们对重点快速复习一下。

假设一个群有三个成员,分别是 uid=101、uid=102 和 uid=103,用户 uid=101 在群里发送消息 “大家好!”,见下图。

IM专题:模型分析(3)—读写扩散模型

那么需要在群消息关系表中保存记录,如下:

IM专题:模型分析(3)—读写扩散模型

也就是,每发送一条群消息,需要站在每个群成员的维度分别保存一条消息记录。这样设计的原因主要有两个:一是为了方便消息的定制化处理(若一个群成员删除了群消息,只是删除了自己的群消息,并不会影响到其他群成员能浏览到该条群消息);二是当群消息数量很大做分库分表时,每一个用户的所有群消息能落在同一张表中,避免对所有数据表的遍历。

一、IM 写扩散模型

上述处理逻辑其实就是 IM 系统的写扩散操作,我们对其抽象出 “写扩散模型”,见下图。

IM专题:模型分析(3)—读写扩散模型

在 IM 的写扩散模型中,每一个群成员都有自己独立的 “信箱”(即消息存储);当产生一条群消息时,需要分别投递(写入)到每个成员的信箱中;每个成员对群消息进行读取时,只需要从自己的信箱中拉取消息即可。

写扩散模型最大的优点就是读取逻辑非常简单,尤其是在大规模用户高并发读取时,避免了锁操作,有较大的性能优势;同时,因为存储隔离,方便了消息的定制化处理。

写扩散模型最大的缺点就是消息的存储成本很大,发送消息操作较为耗时,思考一下,为什么微信群成员数量的上限是 500?同时,写扩散队列在落地实现时有非常高的技术复杂度。

写扩散模型通常应用在群成员数量不高,读多写少的业务场景中。

二、IM 读扩散模型

当一个群成员的数量是 10 或 100 时,我们可以使用 “写扩散模型” 解决方案,当群成员数量是 1000 或 10000 时,写扩散模型会因为冗余写导致非常高的写延时,这肯定是不可取的,此时可以使用 “读扩散模型” 方案,见下图。

IM专题:模型分析(3)—读写扩散模型

在 IM 的读扩散模型中,所有的群成员共享同一个信箱(群消息存储);当产生一条群消息时,只需要将一条群消息记录投递(写入)到信箱中即可;然后所有的群成员从这一个信箱中读取群消息。

看到这里,细心的同学肯定会发出疑问,这样的设计方式,消息的定制化处理怎么做呢?比如:成员 A 删除了一条群消息,这肯定不能影响到其他群成员对这条群消息的浏览。这就是读扩散模型的核心了:需要专门设计一张 “群消息删除表” 来记录删除的群消息id,每个群成员从信箱中读取群消息后,再读取 “群消息删除表”,以此判断该群消息是否展示。

读扩散模型最大的优点就是消息的写入逻辑简单(毕竟只写一条记录即可),消息的存储成本较低,写入的实时性好,没有时延问题。

读扩散模型最大的缺点就是数据的读取逻辑复杂,不仅要读取消息存储表,还要读取其他业务表后进行数据聚合;另外一个非常关键的问题是多用户并发读取时会形成 IO 的热点,造成性能的急剧下降。

读扩散模型通常应用在群成员数量很高,读少写多的业务场景中。

三、读写扩散模型

在 IM 群消息的业务场景中,有一个默认的前提,就是每一个群成员默认订阅了所有的群消息,每一条群消息都会去接收。我们将这个 “订阅” 的前提动作考虑进来,形成完整的读写扩散模型。

读写扩散模型除了 IM 之外,应用更多的是 feed 流场景,比如:微信朋友圈、微博等。

在完整的读写扩散模型中,有【订阅】、【发布】、【取消订阅】等主要的业务动作。

1、订阅

假设一个系统中有 X、Y、Z 三个用户,用户 X 订阅了 用户 Y 和用户 Z,用户 Y 订阅了用户 Z,那么其订阅和被订阅的关系存储,见下图。

IM专题:模型分析(3)—读写扩散模型

订阅与被订阅关系,一般通过双向的 KList 结构来存储。

用户 X 订阅了 Y 和 Z,那么在 “订阅关系” 中,Key 是 X,List 中元素包括 Y 和 Z;用户 Y 订阅了 Z,那么在 “订阅关系” 中,Key 是 Y,List 中元素包括 Z。

在 “被订阅关系” 中,Y 被 X订阅,则 Key 是 Y,List 中元素包括 X;Z 被 X 和 Y 订阅,则 Key 是 Z,List 中元素包括 X 和 Y。

2、发布

假设用户 Z 发布了两条消息:msg1 和 msg2,在不同的扩散模型中,则有不同的存储方式。

在写扩散模型中,处理方式为:

  • 从被订阅关系中,由 Z 获取元素 X 和 Y;

  • 在 “写扩散存储” 中,将两条消息 msg1 和 msg2 分别写入到 X 和 Y 的 KList 结构中,见下图;

  • 用户 X 和 Y 在拉取消息时,直接从 “写扩散存储” 中读取。

IM专题:模型分析(3)—读写扩散模型

在读扩散模型中,处理方式为:

  • 在 “读扩散存储” 中,直接将两条消息 msg1 和 msg2 写入到 Z 的 KList 结构中,见下图;

  • 用户 X 在拉取消息时,首先从 “订阅关系” 中获取其订阅的列表,Y 和 Z;

  • 然后再从 “读扩散存储” 中,分别获取 Y 和 Z 的消息列表后进行聚合。

3、取消订阅

取消订阅操作比较简单,直接从 “订阅关系” 和 “被订阅关系” 的存储列表中删除相关元素,同时根据业务规则,对已经获取的消息是否需要保留进行判断即可

最后,总结文中关键:

  1. IM 写扩散模型中,每产生一条消息,需要分别投递到每个成员的独立信箱中;该模型读取逻辑简单,方便消息的定制化处理,在高并发读时有较大的性能优势;但是该模型的存储成本很大,写扩散队列落地复杂。

  2. IM 读扩散模型中,每产生一条消息,只需写一条记录即可;该模型消息的写入逻辑简单,存储成本低,实时性好;不过数据的读取逻辑复杂,在多用户并发读取时会有 IO 热点问题。

  3. 在完整的读写扩散模型中,有【订阅】、【发布】、【取消订阅】等主要的业务动作;一般通过 KList 结构实现订阅关系、被订阅关系、写扩散存储和读扩散存储。

作者:棕生 | 来源:公众号——架构之魂

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论