单聊核心存储结构全景解析
📋 概述
OpenIM 单聊功能采用客户端-服务端分离的存储架构:
- 客户端存储:基于 SQLite 的本地数据库,实现快速查询和离线访问
- 服务端存储:基于 MongoDB + Redis + Kafka 的分布式存储,保证数据一致性和高可用
为了更好地理解存储结构的设计原理,我们首先通过流程图来分析单聊消息的完整生命周期,然后深入解析每个阶段涉及的存储结构和数据流转。
🔄 消息生命周期与数据流转
在深入分析存储结构之前,我们需要理解单聊消息的完整生命周期。从用户输入文字到对方看到消息并产生已读回执,每个环节都涉及到不同层次的数据存储和状态管理。下面的流程图将帮助我们理解存储设计的业务背景。
1. 消息存储状态变迁流程
stateDiagram-v2
[*] --> Creating : 用户输入
Creating --> Sending : 创建消息对象
Sending --> Processing : 发送到服务端
Processing --> Queued : 投递到Kafka
Queued --> SeqAllocated : MsgTransfer分配Seq
SeqAllocated --> Stored : 存储到MongoDB
Stored --> Pushed : 推送给接收者
Pushed --> Received : 接收者收到
Received --> Read : 用户查看
Read --> Acknowledged : 发送已读回执
Acknowledged --> [*] : 流程完成
state Creating {
ClientMsgID_生成
Status_MsgStatusSending
Seq_0
IsRead_false
}
state Sending {
本地数据库保存
WebSocket发送
等待服务端响应
}
state Processing {
生成ServerMsgID
设置SendTime
校验权限和设置
}
state SeqAllocated {
Redis分配序号
发送者自动已读
MaxSeq更新
ReadSeq更新
}
state Received {
接收者MaxSeq更新
计算未读数
显示红点提醒
}
state Read {
用户查看消息
ReadSeq更新到MaxSeq
红点消失
}
state Acknowledged {
IsRead设置为true
已读时间戳记录
多端状态同步
}
2. 存储层红点逻辑实现
flowchart TD
A[消息事件触发] --> B{消息类型判断}
B -->|新消息到达| C[接收者处理]
B -->|发送消息| D[发送者处理]
B -->|查看消息| E[已读处理]
B -->|同步事件| F[多端同步]
C --> C1[MaxSeq +1]
C1 --> C2[计算未读数]
C2 --> C3{未读数 > 0?}
C3 -->|是| C4[显示红点]
C3 -->|否| C5[隐藏红点]
D --> D1[MaxSeq +1]
D1 --> D2[ReadSeq +1]
D2 --> D3[未读数 = 0]
D3 --> D4[不显示红点]
E --> E1[ReadSeq = MaxSeq]
E1 --> E2[未读数 = 0]
E2 --> E3[红点消失]
E3 --> E4[发送已读回执]
F --> F1[同步MaxSeq和ReadSeq]
F1 --> F2[重新计算未读数]
F2 --> F3[更新红点状态]
C4 --> G[UI更新]
C5 --> G
D4 --> G
E3 --> G
F3 --> G
G --> H[用户看到最新状态]
3. 跨端数据同步存储机制
sequenceDiagram
participant A as 设备A
participant Server as 消息服务器
participant B as 设备B
participant C as 设备C
participant Redis as Redis缓存
participant DB as MongoDB
Note over A,DB: 场景1:设备A发送消息
A->>Server: 发送消息
Server->>Redis: 分配Seq=100
Server->>DB: 存储消息
Server->>Redis: 设置A的ReadSeq=100
par 同步到其他设备
Server->>B: 推送新消息(Seq=100)
Server->>C: 推送新消息(Seq=100)
end
B->>B: MaxSeq=100, ReadSeq=99<br/>未读数=1, 显示红点
C->>C: MaxSeq=100, ReadSeq=99<br/>未读数=1, 显示红点
Note over A,DB: 场景2:设备B查看消息
B->>B: 用户查看消息
B->>Server: 标记已读(ReadSeq=100)
Server->>Redis: 更新B的ReadSeq=100
par 同步已读状态
Server->>A: 已读回执通知
Server->>C: 已读位置同步
end
A->>A: 更新消息IsRead=true<br/>显示"已读"标识
C->>C: ReadSeq=100<br/>未读数=0, 红点消失
Note over A,DB: 场景3:设备C离线后上线
C->>Server: 请求消息同步
Server->>Redis: 获取C的ReadSeq=95
Server->>DB: 查询Seq>95的消息
Server->>C: 返回增量消息
C->>C: 批量处理消息<br/>更新MaxSeq=100<br/>计算未读数=5<br/>显示红点
Note over A,DB: 场景4:全量数据一致性保证
loop 定期同步检查
Server->>Redis: 检查ReadSeq一致性
Server->>DB: 验证消息完整性
Server->>A: 推送缺失消息
Server->>B: 推送缺失消息
Server->>C: 推送缺失消息
end
4. 红点计算的存储设计原理
红点计算公式
OpenIM的红点机制基于精确的数学公式:
未读数 = MaxSeq(会话最大序号) - ReadSeq(用户已读序号)
双重已读状态设计
OpenIM采用双重已读机制确保精确控制:
1. 会话级别已读(ReadSeq)
- 作用:控制红点显示和未读计数
- 存储:Redis缓存 + MongoDB持久化
- 更新时机:用户查看消息、发送消息时
- 影响范围:整个会话的未读数统计
2. 消息级别已读(IsRead)
- 作用:精确标记单条消息的已读状态
- 存储:消息表的isRead字段 + AttachedInfo的HasReadTime
- 更新时机:用户查看消息、收到已读回执时
- 影响范围:单条消息的状态显示(如已读标识)
红点状态变化场景
| 场景 | MaxSeq变化 | ReadSeq变化 | 红点结果 | 说明 |
|---|---|---|---|---|
| 接收新消息 | +1 | 不变 | 出现红点 ✅ | 正常的未读提醒 |
| 查看消息 | 不变 | 设置为MaxSeq | 红点消失 ✅ | 已读后清除红点 |
| 发送消息 | +1 | +1 | 无红点 ✅ | 自己发送不产生红点 |
| 多端同步 | 同步 | 同步 | 一致显示 ✅ | 多设备状态一致 |
以上流程图展示了单聊消息从创建到读取的完整数据流转过程。为了支撑这些复杂的业务逻辑,OpenIM设计了精密的存储架构。接下来我们将详细解析每一层存储结构的设计原理和数据模型。
第一部分:客户端存储层(SQLite)
🗄️ 核心表结构概览
OpenIM SDK 使用三个核心表实现完整的即时通讯数据存储:
| 表名 | 作用 | 数据特点 | 查询频率 |
|---|---|---|---|
| LocalConversation | 会话管理 | 相对稳定,变更较少 | 高频查询 |
| LocalChatLog | 消息存储 | 数据量大,增长快速 | 超高频查询 |
| LocalSendingMessages | 发送状态 | 临时数据,自动清理 | 中频查询 |
📋 1. LocalConversation - 本地会话表
表名: local_conversations
完整字段结构
| 字段名 | 字段类型 | 索引 | 详细描述 |
|---|---|---|---|
| ConversationID | string(128) [主键] | PRIMARY KEY | 会话唯一标识 • 单聊格式: si_${userID1}_${userID2}• 群聊格式: sg_${groupID} 或 n_${groupID}(通知群)• 作为主键,关联消息表 |
| ConversationType | int32 | 无 | 会话类型枚举 • 1 = 单聊会话 (SINGLE_CHAT)• 2 = 群聊会话 (GROUP_CHAT)• 3 = 超级群聊 (SUPER_GROUP_CHAT)• 4 = 通知会话 (NOTIFICATION) |
| UserID | string(64) | 无 | 单聊对方用户ID • 快速定位聊天对象 • 群聊中此字段为空 |
| GroupID | string(128) | 无 | 群聊群组ID • 群聊会话中的群组标识 • 单聊中此字段为空 |
| ShowName | string(255) | 无 | 会话显示名称 • 对方昵称或备注名 • 群名称 • 影响会话列表显示 |
| FaceURL | string(255) | 无 | 会话头像URL • 对方头像地址 • 群头像地址 • 支持本地缓存 |
| RecvMsgOpt | int32 | 无 | 接收消息选项枚举 • 0 = 正常接收 (NORMAL)• 1 = 不接收消息 (NOT_RECEIVE)• 2 = 接收但不提醒 (NOT_NOTIFY) |
| UnreadCount | int32 | 无 | 未读消息数量 • 实时更新计数 • 避免实时统计查询 • 范围:0 到 999+ |
| GroupAtType | int32 | 无 | 群组@类型枚举 • 0 = 无@ (NO_AT)• 1 = @所有人 (AT_ALL)• 2 = @我 (AT_ME)• 3 = @所有人且@我 (AT_ALL_AND_ME) |
| LatestMsg | string(1000) | 无 | 最新消息JSON • 会话列表预览 • 提升性能 • JSON格式存储完整消息结构 |
| LatestMsgSendTime | int64 | INDEX | 最新消息时间戳 • 会话列表排序依据 • 毫秒级时间戳 • 关键索引字段 |
| DraftText | string | 无 | 草稿文本内容 • 用户输入但未发送的文本 • 支持富文本格式 |
| DraftTextTime | int64 | 无 | 草稿文本时间戳 • 草稿创建/更新时间 • 用于草稿过期清理 |
| IsPinned | bool | 无 | 是否置顶 • true = 置顶会话优先显示 • false = 正常排序 |
| IsPrivateChat | bool | 无 | 是否私密聊天 • true = 阅后即焚功能开启 • false = 正常聊天模式 |
| BurnDuration | int32 | 无 | 阅后即焚时长 • 默认30秒 • 范围:10-604800秒(7天) • 仅在IsPrivateChat=true时生效 |
| IsNotInGroup | bool | 无 | 是否不在群组中 • true = 已退群但保留会话 • false = 正常在群状态 • 仅群聊会话有效 |
| UpdateUnreadCountTime | int64 | 无 | 未读数更新时间戳 • 未读数最后更新时间 • 用于增量同步优化 |
| AttachedInfo | string(1024) | 无 | 附加信息 • 扩展业务数据 • JSON格式存储 |
| Ex | string(1024) | 无 | 扩展字段 • 自定义业务数据 • 预留扩展能力 |
| MaxSeq | int64 | 无 | 最大消息序列号 • 增量同步边界 • 服务端同步使用 |
| MinSeq | int64 | 无 | 最小消息序列号 • 清理边界控制 • 历史消息管理 |
| MsgDestructTime | int64 | 无 | 消息销毁时间 • 默认604800秒(7天) • 消息自动清理时间 • 范围:3600-31536000秒(1小时-1年) |
| IsMsgDestruct | bool | 无 | 是否开启消息销毁 • true = 开启自动销毁 • false = 永久保存 • 默认false |
📨 2. LocalChatLog - 本地聊天记录表
表名: local_chat_logs
完整字段结构
| 字段名 | 字段类型 | 索引 | 详细描述 |
|---|---|---|---|
| ClientMsgID | string(64) [主键] | PRIMARY KEY | 客户端消息ID • UUID格式,客户端生成 • 消息去重和状态跟踪 • 格式:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| ServerMsgID | string(64) | 无 | 服务端消息ID • 服务器分配的全局唯一ID • 多端同步保证 • 消息投递后由服务端返回 |
| SendID | string(64) | 无 | 发送者用户ID • 消息归属和权限判断 • 与用户表关联 |
| RecvID | string(64) | INDEX | 接收者ID • 重要索引字段 • 单聊为对方用户ID • 群聊为群组ID |
| SenderPlatformID | int32 | 无 | 发送者平台ID枚举 • 1 = iOS• 2 = Android• 3 = Windows• 4 = OSX• 5 = Web• 6 = 小程序• 7 = Linux• 8 = iPad• 9 = Android Pad |
| SenderNickname | string(255) | 无 | 发送者昵称快照 • 避免改名后显示异常 • 历史消息显示使用 |
| SenderFaceURL | string(255) | 无 | 发送者头像快照 • 保持历史一致性 • 防止头像变更影响历史显示 |
| SessionType | int32 | 无 | 会话类型枚举 • 1 = 单聊消息 (SINGLE_CHAT)• 2 = 群聊消息 (GROUP_CHAT)• 3 = 超级群聊 (SUPER_GROUP_CHAT)• 4 = 通知消息 (NOTIFICATION) |
| MsgFrom | int32 | 无 | 消息来源枚举 • 100 = 用户发送 (USER)• 200 = 系统通知 (ADMIN)• 300 = 机器人 (BOT) |
| ContentType | int32 | INDEX | 消息内容类型枚举 • 101 = 文本消息 (TEXT)• 102 = 图片消息 (PICTURE)• 103 = 语音消息 (SOUND)• 104 = 视频消息 (VIDEO)• 105 = 文件消息 (FILE)• 106 = @消息 (AT_TEXT)• 107 = 合并转发 (MERGE)• 108 = 名片消息 (CARD)• 109 = 位置消息 (LOCATION)• 110 = 自定义消息 (CUSTOM)• 111 = 撤回消息通知 (REVOKE)• 112 = 阅读已读回执 (C2C_READ_RECEIPT)• 113 = 输入状态 (TYPING)• 114 = 引用消息 (QUOTE)• 115 = 表情回复 (FACE)• 116 = 高级文本 (ADVANCED_TEXT)• 系统通知类型(1000+) • 1001 = 好友申请 (FRIEND_APPLICATION_APPROVED)• 1002 = 好友申请被拒绝• 1003 = 好友被删除• 1004 = 群创建通知• 1005 = 群信息变更• 1006 = 群成员邀请• 1007 = 群成员移除• 1008 = 群解散通知• 1009 = 用户入群申请• 1010 = 用户退群通知 |
| Content | string(1000) | 无 | 消息内容JSON • 根据ContentType解析 • 支持复杂数据结构 • 各类型具体格式见下文 |
| IsRead | bool | 无 | 是否已读标记 • true = 已读消息 • false = 未读消息 • 影响未读数统计 |
| Status | int32 | 无 | 消息发送状态枚举 • 1 = 发送中 (SENDING)• 2 = 发送成功 (SEND_SUCCESS)• 3 = 发送失败 (SEND_FAILURE)• 4 = 已删除 (DELETED)• 5 = 已撤回 (REVOKED) |
| Seq | int64 | INDEX | 消息序列号 • 服务器分配,单调递增 • 消息排序和同步依据 • 会话级别唯一 |
| SendTime | int64 | INDEX | 发送时间戳 • 毫秒级精度 • 支持时间范围查询 • 消息排序依据 |
| CreateTime | int64 | 无 | 创建时间戳 • 消息在本地数据库创建时间 • 用于本地排序和清理 |
| AttachedInfo | string(1024) | 无 | 附加信息 • 扩展业务数据 • 已读时间、反应信息等 |
| Ex | string(1024) | 无 | 扩展字段 • 自定义业务数据 • 预留扩展能力 |
| LocalEx | string(1024) | 无 | 本地扩展字段 • 仅客户端使用 • 不同步到服务端 • 本地状态存储 |
🚀 3. LocalSendingMessages - 发送状态表
表名: local_sending_messages
完整字段结构
| 字段名 | 字段类型 | 索引 | 详细描述 |
|---|---|---|---|
| ConversationID | string(128) [联合主键] | PRIMARY KEY | 会话标识 • 批量处理发送中消息 • 与LocalConversation表关联 |
| ClientMsgID | string(64) [联合主键] | PRIMARY KEY | 客户端消息ID • 定位具体发送中消息 • 与LocalChatLog表关联 |
| Ex | string(1024) | 无 | 扩展信息 • 重发次数、失败原因等 • JSON格式存储发送状态详情 • 包含:重试次数、最后错误码、下次重试时间等 |
第二部分:服务端存储层
🏗️ 架构概览
服务端采用三层存储架构:
- MongoDB:持久化存储,保证数据安全
- Redis:高速缓存,提升查询性能
- Kafka:异步消息队列,实现系统解耦
🗄️ MongoDB 数据库存储层
1. Conversation - 会话核心表
集合名: conversation
完整字段结构
| 字段名 | 字段类型 | 索引 | 详细描述 |
|---|---|---|---|
| _id | ObjectId | PRIMARY KEY | MongoDB主键 • 自动生成的唯一标识 |
| OwnerUserID | string | INDEX | 会话拥有者ID • 每个用户都有自己的会话副本 • 多端同步的基础 • 复合索引:owner_user_id + conversation_id |
| ConversationID | string | INDEX | 会话唯一标识 • 单聊格式: si_${userID1}_${userID2}• 群聊格式: sg_${groupID}• 通知格式: n_${groupID}• 与客户端保持一致 |
| ConversationType | int32 | 无 | 会话类型枚举 • 1 = 单聊会话 (SINGLE_CHAT)• 2 = 群聊会话 (GROUP_CHAT)• 3 = 超级群聊 (SUPER_GROUP_CHAT)• 4 = 通知会话 (NOTIFICATION) |
| UserID | string | 无 | 单聊对方用户ID • 单聊中的另一方用户 • 群聊中为空 |
| GroupID | string | 无 | 群聊群组ID • 群聊会话中的群组标识 • 单聊中为空 |
| RecvMsgOpt | int32 | 无 | 消息接收选项枚举 • 0 = 正常接收 (NORMAL)• 1 = 不接收消息 (NOT_RECEIVE)• 2 = 接收不提醒 (NOT_NOTIFY) |
| IsPinned | bool | 无 | 是否置顶 • true = 置顶会话 • false = 正常会话 • 影响会话列表排序 |
| IsPrivateChat | bool | 无 | 是否私密聊天 • true = 阅后即焚功能开启 • false = 正常聊天模式 |
| BurnDuration | int32 | 无 | 阅后即焚时长 • 单位:秒 • 默认:30秒 • 范围:10-604800秒 |
| GroupAtType | int32 | 无 | 群组@类型枚举 • 0 = 无@ (NO_AT)• 1 = @所有人 (AT_ALL)• 2 = @我 (AT_ME)• 3 = @所有人且@我 (AT_ALL_AND_ME) |
| AttachedInfo | string | 无 | 附加信息 • 扩展业务数据JSON字符串 |
| Ex | string | 无 | 扩展字段 • 自定义业务数据 |
| MaxSeq | int64 | 无 | 最大消息序列号 • 增量同步使用 • 与SeqConversation表同步 |
| MinSeq | int64 | 无 | 最小消息序列号 • 清理边界控制 • 历史消息管理 |
| CreateTime | time.Time | 无 | 创建时间 • MongoDB时间类型 • 会话创建时间戳 |
| IsMsgDestruct | bool | 无 | 是否开启消息销毁 • true = 开启自动销毁 • false = 永久保存 |
| MsgDestructTime | int64 | 无 | 消息销毁时长 • 单位:秒 • 默认:604800(7天) |
| LatestMsgDestructTime | time.Time | 无 | 最新消息销毁时间 • 用于批量清理消息 |
2. SeqConversation - 会话序列号表
集合名: seq
完整字段结构
| 字段名 | 字段类型 | 索引 | 详细描述 |
|---|---|---|---|
| _id | ObjectId | PRIMARY KEY | MongoDB主键 |
| ConversationID | string | UNIQUE INDEX | 会话标识 • 唯一索引,快速定位序列号信息 • 与会话表关联的关键字段 |
| MaxSeq | int64 | 无 | 最大序列号 • 新消息seq分配依据 • 原子递增操作保证唯一性 • 初始值:0 |
| MinSeq | int64 | 无 | 最小序列号 • 历史消息边界控制 • 消息清理使用 • 初始值:0 |
3. SeqUser - 用户序列号表
集合名: seq_user
完整字段结构
| 字段名 | 字段类型 | 索引 | 详细描述 |
|---|---|---|---|
| _id | ObjectId | PRIMARY KEY | MongoDB主键 |
| UserID | string | INDEX | 用户ID • 多端同步标识 • 复合索引:user_id + conversation_id |
| ConversationID | string | INDEX | 会话ID • 多会话管理 • 与SeqConversation表关联 |
| MinSeq | int64 | 无 | 用户最小序列号 • 增量拉取边界 • 用户侧消息清理边界 • 初始值:0 |
| MaxSeq | int64 | 无 | 用户最大序列号 • 断点续传使用 • 标识用户已同步的最大seq • 初始值:0 |
| ReadSeq | int64 | 无 | 已读序列号 • 未读数计算公式:MaxSeq - ReadSeq • 已读回执功能基础 • 初始值:0 |
4. 消息存储结构
MsgDocModel - 消息文档模型
集合名: msg
文档结构
{
"_id": ObjectId,
"doc_id": "会话ID:文档索引",
"msgs": [
{
"msg": MsgDataModel,
"revoke": RevokeModel,
"del_list": ["删除用户ID列表"],
"is_read": boolean
}
]
}
MsgInfoModel - 消息信息模型
| 字段名 | 字段类型 | 详细描述 |
|---|---|---|
| msg | MsgDataModel | 消息主体数据 • 包含完整消息内容和元数据 • 详细结构见MsgDataModel |
| revoke | RevokeModel | 撤回信息 • 消息被撤回时不为空 • 正常消息为null • 详细结构见RevokeModel |
| del_list | []string | 删除用户列表 • 记录删除此消息的用户ID数组 • 单聊中最多包含2个用户ID • 实现"删除消息"功能,不同用户可独立删除 |
| is_read | bool | 全员已读标记 • true = 消息已被所有接收者读取 • false = 仍有用户未读 • 单聊中表示对方是否已读 |
RevokeModel - 撤回信息模型
| 字段名 | 字段类型 | 详细描述 |
|---|---|---|
| role | int32 | 撤回者角色枚举 • 1 = 普通用户 (NORMAL_USER)• 2 = 群管理员 (GROUP_ADMIN)• 3 = 群主 (GROUP_OWNER)• 100 = 系统管理员 (SYSTEM_ADMIN) |
| user_id | string | 撤回者用户ID • 执行撤回操作的用户 • 撤回权限判断依据 |
| nickname | string | 撤回者昵称 • 撤回时的昵称快照 • 用于撤回通知显示 |
| time | int64 | 撤回时间 • 消息被撤回的时间戳(毫秒) • 用于撤回时效判断 |
MsgDataModel - 消息数据模型
| 字段名 | 字段类型 | 详细描述 |
|---|---|---|
| send_id | string | 发送者ID • 对应会话表中的OwnerUserID • 权限验证和展示 |
| recv_id | string | 接收者ID • 单聊为对方用户ID • 群聊为群组ID • 消息路由使用 |
| group_id | string | 群组ID • 群聊消息的群组标识 • 单聊中为空字符串 |
| conversation_id | string | 会话标识 • 关联会话表中的ConversationID • 消息归档和查询索引 |
| client_msg_id | string | 客户端消息ID • 与客户端LocalChatLog表对应 • 去重和状态跟踪 • UUID格式 |
| server_msg_id | string | 服务端消息ID • 服务端生成的全局唯一ID • MongoDB ObjectId字符串形式 |
| sender_platform_id | int32 | 发送者平台ID枚举 • 1 = iOS, 2 = Android, 3 = Windows• 4 = OSX, 5 = Web, 6 = 小程序• 7 = Linux, 8 = iPad, 9 = Android Pad |
| sender_nickname | string | 发送者昵称 • 发送时的昵称快照 • 避免显示异常 |
| sender_face_url | string | 发送者头像URL • 发送时的头像快照 • 历史一致性保证 |
| session_type | int32 | 会话类型 • 与ConversationType对应 • 1 = 单聊, 2 = 群聊, 3 = 超级群聊, 4 = 通知 |
| msg_from | int32 | 消息来源枚举 • 100 = 用户发送 (USER)• 200 = 系统通知 (ADMIN)• 300 = 机器人 (BOT) |
| content_type | int32 | 内容类型 • 与客户端ContentType完全对应 • 详细枚举见客户端LocalChatLog表 |
| content | string | 消息内容 • JSON格式,与客户端保持一致 • 根据content_type解析不同结构 |
| seq | int64 | 消息序列号 • 对应会话表中的MaxSeq • 全局单调递增,排序和同步依据 |
| send_time | int64 | 发送时间 • 与客户端SendTime对应 • 毫秒级时间戳 |
| create_time | int64 | 创建时间 • 服务端创建时间戳 • 用于服务端排序和清理 |
| status | int32 | 消息状态 • 与客户端Status对应 • 1 = 发送中, 2 = 成功, 3 = 失败 |
| is_read | bool | 是否已读 • 与客户端IsRead对应 • 个人已读状态标记 |
| options | map[string]bool | 消息选项配置 • 详细配置见msgprocessor/options.go • 控制消息行为:存储、推送、同步等 |
| offline_push | OfflinePushModel | 离线推送配置 • 推送标题、内容、扩展信息等 |
| at_user_id_list | []string | @用户ID列表 • 群聊@功能使用 • 空数组表示无@用户 |
| attached_info | string | 附加信息 • 扩展业务数据JSON字符串 |
| ex | string | 扩展字段 • 自定义业务数据 |
OfflinePushModel - 离线推送模型
| 字段名 | 字段类型 | 详细描述 |
|---|---|---|
| title | string | 推送标题 • 显示在通知栏的标题 • 默认为发送者昵称 |
| desc | string | 推送描述 • 推送内容摘要 • 默认为消息内容预览 |
| ex | string | 推送扩展信息 • 自定义推送数据 |
| ios_push_sound | string | iOS推送声音 • iOS通知声音文件名 • 默认为系统声音 |
| ios_badge_count | bool | iOS角标计数 • true = 显示角标数字 • false = 不显示角标 |
🚀 Redis 缓存层
OpenIM Redis缓存采用分层过期策略,平衡性能与数据一致性:
1. 会话相关缓存
| 缓存Key格式 | 数据类型 | TTL时间 | 缓存内容 | 使用场景 |
|---|---|---|---|---|
CONVERSATION:{ownerUserID}:{conversationID} | Hash | 12小时 | 完整会话信息 | 会话详情快速查询 |
CONVERSATION_IDS:{ownerUserID} | Set | 12小时 | 用户所有会话ID集合 | 会话列表加载 |
CONVERSATION_IDS_HASH:{ownerUserID} | String | 12小时 | 会话ID列表数组json | 增量同步检测 |
| 会话级别消息控制 | ||||
CONVERSATION_USER_MAX:{userID} | Hash | 12小时 | 用户各会话版本信息 | 增量同步 |
CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:{conversationID} | Set | 12小时 | 不接收消息的用户集合 | 消息投递过滤 |
2. 消息相关缓存
| 缓存Key格式 | 数据类型 | TTL时间 | 缓存内容 | 使用场景 |
|---|---|---|---|---|
MSG_CACHE:{conversationID}:{seq} | Hash | 24小时 | 完整消息数据 | 消息快速查询 |
3. 序列号相关缓存
| 缓存Key格式 | 数据类型 | TTL时间 | 缓存内容 | 使用场景 |
|---|---|---|---|---|
MALLOC_SEQ:{conversationID}:CURR | Hash | 1年 | CURR:会话当前序列号 LAST:会话最后分配序列号 TIME:序列号分配时间戳 | 新消息seq分配 |
SEQ_USER_READ:{conversationID}:{userID} | String | 30天 | 用户已读序列号 | 已读回执功能 |
📨 Kafka 消息队列
核心Topic配置
| Topic名称 | 作用 | 分区数 | 副本数 | 分区策略 | 消费者组 |
|---|---|---|---|---|---|
| toRedisTopic | 消息到Redis缓存 | 8 | 3 | 按conversationID哈希 | toRedisGroupID |
| toMongoTopic | 消息到MongoDB持久化 | 8 | 3 | 按conversationID哈希 | toMongoGroupID |
| toPushTopic | 在线用户推送 | 8 | 3 | 按userID哈希 | toPushGroupID |
| toOfflinePushTopic | 离线用户推送 | 8 | 3 | 按userID哈希 | toOfflineGroupID |