Skip to content

Commit e6105c1

Browse files
committed
feat: 🎸 new post
1 parent ecde1e7 commit e6105c1

7 files changed

Lines changed: 83 additions & 3 deletions

content/posts/1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
+++
22
date = '2025-08-03T15:31:31+08:00'
3-
draft = false
3+
draft = true
44
title = 'Core Principles and Differences of Function Calling, MCP, and A2A'
55
+++
66

content/posts/3.md

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,84 @@ draft = false
44
title = '【IM专题】01-消息存储设计方案'
55
+++
66

7-
![图片描述](/images/wxsync-2023-05-5661e69baff774c2c2c90b5baf71f18c.png)
7+
即时通讯系统面对的核心挑战是海量消息的高效存储和可靠同步
8+
9+
![](/images/1760555202788-1dced840-5ce4-46a3-bffc-3d09785e64fd.png)
10+
11+
假设当前有一个群组A,里面有三个用户,此时uid=101的用户在群中发送了一条消息,消息该如何存储?
12+
13+
# 方案一:写扩散(Push Model / 推送模型)
14+
对数据进行写入操作时,有更多的写入动作。每发送一条群消息,系统需要站在每个群成员的维度分别保存一条消息记录。
15+
16+
![](/images/1760561205294-df9183ab-e468-491e-aa3a-6bfebf01e421.png)
17+
18+
写扩散的好处:
19+
20+
1. 便于读取:避免复杂的合并与排序。
21+
2. 方便消息的定制化处理:若一个群成员删除了群消息,只是删除了自己的群消息,并不会影响到其他群成员能浏览到该条群消息;方便支持消息的“已读”状态独立标记。
22+
3. 便于分库分表:每一个用户的所有群消息能落在同一张表中,避免对所有数据表的遍历。
23+
24+
25+
26+
适用场景:私聊、小群聊(群成员数量少)。
27+
28+
主要限制:当群成员数量非常多时,写扩散模型会因为冗余写入导致极高的写延迟和系统写操作压力。
29+
30+
31+
32+
# 方案二:读扩散(Pull Model / 拉取模型)
33+
对数据进行读取操作时,有更多的读取动作。每发送一条群消息,只在库中存入一条共享消息记录。
34+
35+
36+
37+
读扩散的优势:
38+
39+
1. 高写入效率
40+
2. 实时修改和权限控制更简单:消息如果需要修改或撤回,只需要update一条记录
41+
42+
缺点:
43+
44+
1. 读取压力大:
45+
+ 多源查询和排序的开销:当用户拉取全局未读消息列表时,需要依次遍历自己所有活跃会话的Timeline,以获取每个会话的最新一条消息。
46+
+ 全局排序:在取到所有最新消息后,必须按照消息时间戳进行全局排序,才能按时间顺序对主会话列表进行排序和展示,这是一个CPU密集型操作,极大地增加了读取延迟。
47+
2. 消息定制化处理困难:因为消息是共享存储的,任何针对消息状态的个性化处理都需要引入额外的存储和逻辑处理。例如,删除自己的消息记录,需要引入一个独立的状态表,存储“用户-会话-消息”的删除标记;消息已读标记,可以引入一个已读游标,记录用户在某个会话中已读到的最新消息ID。
48+
49+
50+
51+
# 扩展
52+
读写扩散模型除了 IM 之外,应用更多的是 feed 流场景,比如:微信朋友圈、微博等。
53+
54+
在完整的读写扩散模型中,有【订阅】、【发布】、【取消订阅】等主要的业务动作
55+
56+
57+
58+
## 1 订阅
59+
假设一个系统中有 X、Y、Z 三个用户,用户 X 订阅了 用户 Y 和用户 Z,用户 Y 订阅了用户 Z,那么其订阅和被订阅的关系存储,见下图。
60+
61+
![](/images/1760561368890-ea9aeef6-d031-49f8-b4eb-d8022591dcfb.png)
62+
63+
订阅与被订阅关系,一般通过双向的 KList 结构来存储。
64+
65+
用户 X 订阅了 Y 和 Z,那么在 “订阅关系” 中,Key 是 X,List 中元素包括 Y 和 Z;用户 Y 订阅了 Z,那么在 “订阅关系” 中,Key 是 Y,List 中元素包括 Z。
66+
67+
在 “被订阅关系” 中,Y 被 X订阅,则 Key 是 Y,List 中元素包括 X;Z 被 X 和 Y 订阅,则 Key 是 Z,List 中元素包括 X 和 Y。
68+
69+
## 2 发布
70+
假设用户 Z 发布了两条消息:msg1 和 msg2,在不同的扩散模型中,则有不同的存储方式。
71+
72+
在写扩散模型中,处理方式为:
73+
74+
+ 从被订阅关系中,由 Z 获取元素 X 和 Y;
75+
+ 在 “写扩散存储” 中,将两条消息 msg1 和 msg2 分别写入到 X 和 Y 的 KList 结构中,见下图;
76+
+ 用户 X 和 Y 在拉取消息时,直接从 “写扩散存储” 中读取。
77+
78+
![](/images/1760561368914-081c43bb-cc74-4dd3-a2e3-7ccaf578ace7.png)
79+
80+
在读扩散模型中,处理方式为:
81+
82+
+ 在 “读扩散存储” 中,直接将两条消息 msg1 和 msg2 写入到 Z 的 KList 结构中,见下图;
83+
+ 用户 X 在拉取消息时,首先从 “订阅关系” 中获取其订阅的列表,Y 和 Z;
84+
+ 然后再从 “读扩散存储” 中,分别获取 Y 和 Z 的消息列表后进行聚合。
85+
86+
## 3 取消订阅
87+
取消订阅操作比较简单,直接从 “订阅关系” 和 “被订阅关系” 的存储列表中删除相关元素,同时根据业务规则,对已经获取的消息是否需要保留进行判断即可。

content/posts/my-first-post.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
+++
22
title = 'My First Post'
33
date = 2024-01-14T07:07:07+01:00
4-
draft = false
4+
draft = true
55
+++
66
## Introduction
77

15.3 KB
Loading
46.5 KB
Loading
10.4 KB
Loading
9.14 KB
Loading

0 commit comments

Comments
 (0)