如何存储消息的状态(发送中、已发送、已送达、已读)?
Posted: Tue May 20, 2025 11:02 am
在像 WhatsApp 这样的消息传递应用中,消息状态的存储是确保消息可靠递送和提供用户信心的关键环节。这些状态通常包括“发送中”、“已发送”、“已送达”和“已读”,它们在用户界面中以不同的勾选符号(例如,单勾、双勾、蓝勾)表示。
存储这些状态需要考虑实时更新、数据一致性、可扩展性和不同聊天类型(一对一和群聊)的复杂性。
1. 消息状态的类型及其含义
发送中(Pending/Sending):
含义: 消息已从发送方设备发出,但尚未被服务器确认接收或处理。
显示: 通常无勾选或显示为时钟图标。
存储位置: 主要在发送方设备的本地数据库中,作为临时状态。服务器端可能没有这个状态,或者只在消息队列中短暂存在。
已发送(Sent):
含义: 消息已成功从发送方设备发出,并被 WhatsApp 服务器接收和记录。服务器已确认收到消息,并准备将其推送到接收方。
显示: 单勾。
存储位置: 发送方设备的本地数据库会更新此状态。服务器端在将消息推送到接收方(或临时存储给离线接收方)时,也会有对应的记录。
已送达(Delivered):
含义: 消息已成功从 WhatsApp 服务器推送并送达到接收方设备。
显示: 双勾。
存储位置:
接收方设备的本地数据库: 消息被 巴林 whatsapp 数据库 入接收方设备的本地数据库后,会触发确认回执(ACK)给服务器。
服务器端: 服务器接收到递送回执后,会更新消息状态,并将此状态通知发送方。
发送方设备的本地数据库: 最终发送方设备的本地数据库会更新此状态。
已读(Read/Seen):
含义: 接收方已打开并阅读了这条消息。
显示: 蓝勾(通常是双勾变蓝)。
存储位置:
接收方设备的本地数据库: 当消息在屏幕上可见时,客户端通常会触发已读回执。
服务器端: 服务器接收到已读回执后,会更新消息状态,并将此状态通知发送方。
发送方设备的本地数据库: 最终发送方设备的本地数据库会更新此状态。
2. 存储方式:核心消息表 + 状态/回执表
消息状态通常存储在消息表(Messages table)的字段中,但在处理群聊的“谁已读”这种复杂情况时,可能需要额外的回执表。
a. 在核心 Messages 表中存储(适用于一对一和基本状态)
对于一对一聊天,最简单的方式是在**Messages 表中添加一个 status 字段**。
工作原理:
当消息被发送并进入服务器时,Messages 表的 status 字段可能设为 SENT。
对于一对一消息,当接收方设备确认收到消息时,服务器会直接更新 Messages 表的 status 为 DELIVERED。当接收方阅读时,再更新为 READ。
对于群聊消息,服务器会将消息分发给所有群成员。当每个成员的设备收到消息时,会向服务器发送一个递送回执,服务器则在 MessageReceipts 表中插入一条新记录:(message_id, recipient_id, 'DELIVERED', timestamp)。
当某个成员阅读消息时,其设备会发送已读回执,服务器则在 MessageReceipts 表中更新或插入:(message_id, recipient_id, 'READ', timestamp)。
优点: 精确追踪每个接收者的状态,尤其适用于群聊的复杂性,支持“查看消息详情”功能,可以显示哪些成员已送达/已读。
缺点: 增加了数据量和复杂性,需要更多的写入操作。
3. 同步与一致性考量
消息状态的更新是一个典型的最终一致性场景:
发送方发出消息 -> 状态变为“发送中”(本地)
服务器接收 -> 状态变为“已发送”(本地和服务器)
接收方设备接收 -> 状态变为“已送达”(本地),接收方设备发送回执
服务器接收递送回执 -> 状态更新为“已送达”,并通知发送方设备
发送方设备接收通知 -> 状态更新为“已送达”(本地)
接收方阅读消息 -> 状态变为“已读”(本地),接收方设备发送回执
服务器接收已读回执 -> 状态更新为“已读”,并通知发送方设备
发送方设备接收通知 -> 状态更新为“已读”(本地)
这个过程涉及到多个节点之间的通信和状态更新,数据会经历一个不一致窗口,但最终会达到一致状态。
4. 优化与挑战
高并发写入: 大量消息和回执的写入会产生巨大的数据库压力,需要高度可扩展的存储方案(如分片)。
实时通知: 状态变化需要通过实时推送机制(如 WebSockets 或 MQTT)迅速通知发送方。
数据量: MessageReceipts 表可能会变得非常庞大,需要高效的索引和可能的归档策略。
去重和幂等性: 由于网络原因,回执消息可能重复发送,服务器需要处理幂等性,避免重复更新或创建记录。
综上所述,WhatsApp 存储消息状态会采用组合策略:核心消息表(Messages)存储基本状态,而独立的 MessageReceipts 表则用于处理群聊中每个成员的递送和已读状态。整个过程通过客户端、服务器和设备之间的回执机制实现,并遵循最终一致性原则,以应对其庞大的用户基数和高并发需求。
存储这些状态需要考虑实时更新、数据一致性、可扩展性和不同聊天类型(一对一和群聊)的复杂性。
1. 消息状态的类型及其含义
发送中(Pending/Sending):
含义: 消息已从发送方设备发出,但尚未被服务器确认接收或处理。
显示: 通常无勾选或显示为时钟图标。
存储位置: 主要在发送方设备的本地数据库中,作为临时状态。服务器端可能没有这个状态,或者只在消息队列中短暂存在。
已发送(Sent):
含义: 消息已成功从发送方设备发出,并被 WhatsApp 服务器接收和记录。服务器已确认收到消息,并准备将其推送到接收方。
显示: 单勾。
存储位置: 发送方设备的本地数据库会更新此状态。服务器端在将消息推送到接收方(或临时存储给离线接收方)时,也会有对应的记录。
已送达(Delivered):
含义: 消息已成功从 WhatsApp 服务器推送并送达到接收方设备。
显示: 双勾。
存储位置:
接收方设备的本地数据库: 消息被 巴林 whatsapp 数据库 入接收方设备的本地数据库后,会触发确认回执(ACK)给服务器。
服务器端: 服务器接收到递送回执后,会更新消息状态,并将此状态通知发送方。
发送方设备的本地数据库: 最终发送方设备的本地数据库会更新此状态。
已读(Read/Seen):
含义: 接收方已打开并阅读了这条消息。
显示: 蓝勾(通常是双勾变蓝)。
存储位置:
接收方设备的本地数据库: 当消息在屏幕上可见时,客户端通常会触发已读回执。
服务器端: 服务器接收到已读回执后,会更新消息状态,并将此状态通知发送方。
发送方设备的本地数据库: 最终发送方设备的本地数据库会更新此状态。
2. 存储方式:核心消息表 + 状态/回执表
消息状态通常存储在消息表(Messages table)的字段中,但在处理群聊的“谁已读”这种复杂情况时,可能需要额外的回执表。
a. 在核心 Messages 表中存储(适用于一对一和基本状态)
对于一对一聊天,最简单的方式是在**Messages 表中添加一个 status 字段**。
工作原理:
当消息被发送并进入服务器时,Messages 表的 status 字段可能设为 SENT。
对于一对一消息,当接收方设备确认收到消息时,服务器会直接更新 Messages 表的 status 为 DELIVERED。当接收方阅读时,再更新为 READ。
对于群聊消息,服务器会将消息分发给所有群成员。当每个成员的设备收到消息时,会向服务器发送一个递送回执,服务器则在 MessageReceipts 表中插入一条新记录:(message_id, recipient_id, 'DELIVERED', timestamp)。
当某个成员阅读消息时,其设备会发送已读回执,服务器则在 MessageReceipts 表中更新或插入:(message_id, recipient_id, 'READ', timestamp)。
优点: 精确追踪每个接收者的状态,尤其适用于群聊的复杂性,支持“查看消息详情”功能,可以显示哪些成员已送达/已读。
缺点: 增加了数据量和复杂性,需要更多的写入操作。
3. 同步与一致性考量
消息状态的更新是一个典型的最终一致性场景:
发送方发出消息 -> 状态变为“发送中”(本地)
服务器接收 -> 状态变为“已发送”(本地和服务器)
接收方设备接收 -> 状态变为“已送达”(本地),接收方设备发送回执
服务器接收递送回执 -> 状态更新为“已送达”,并通知发送方设备
发送方设备接收通知 -> 状态更新为“已送达”(本地)
接收方阅读消息 -> 状态变为“已读”(本地),接收方设备发送回执
服务器接收已读回执 -> 状态更新为“已读”,并通知发送方设备
发送方设备接收通知 -> 状态更新为“已读”(本地)
这个过程涉及到多个节点之间的通信和状态更新,数据会经历一个不一致窗口,但最终会达到一致状态。
4. 优化与挑战
高并发写入: 大量消息和回执的写入会产生巨大的数据库压力,需要高度可扩展的存储方案(如分片)。
实时通知: 状态变化需要通过实时推送机制(如 WebSockets 或 MQTT)迅速通知发送方。
数据量: MessageReceipts 表可能会变得非常庞大,需要高效的索引和可能的归档策略。
去重和幂等性: 由于网络原因,回执消息可能重复发送,服务器需要处理幂等性,避免重复更新或创建记录。
综上所述,WhatsApp 存储消息状态会采用组合策略:核心消息表(Messages)存储基本状态,而独立的 MessageReceipts 表则用于处理群聊中每个成员的递送和已读状态。整个过程通过客户端、服务器和设备之间的回执机制实现,并遵循最终一致性原则,以应对其庞大的用户基数和高并发需求。