tencent cloud

消息队列 Pulsar 版

动态与公告
新功能发布记录
集群版本更新记录
产品公告
产品简介
TDMQ 产品系列介绍与选型
什么是消息队列 Pulsar 版
产品优势
应用场景
技术原理
产品系列
开源 Pulsar 版本支持说明
与开源 Pulsar 对比
高可用
配额与限制
基础概念
产品计费
计费概述
价格说明
计费示例
续费说明
查看消费明细
欠费说明
退费说明
快速入门
入门流程指引
准备工作
使用 SDK 收发普通消息
使用 SDK 收发高级特性消息
用户指南
使用流程指引
配置账号权限
新建集群
配置命名空间
配置 Topic
连接集群
管理集群
查询消息及轨迹
跨地域复制
查看监控和配置告警
实践教程
客户端使用实践
异常消费者隔离
限流机制说明
交易对账
消息幂等性
消息压缩
迁移指南
单写多读集群迁移方案
虚拟集群平滑迁移至专业集群
API 参考
API 概览
SDK 参考
SDK 概述
SDK 配置参数推荐
TCP 协议(Pulsar 社区版)
安全与合规
权限管理
删除保护
云 API 审计
常见问题
监控相关
客户端相关
服务协议
服务等级协议
TDMQ 政策
联系我们
词汇表

消息幂等性

PDF
聚焦模式
字号
最后更新时间: 2025-12-24 15:24:31
应用的幂等是在分布式系统设计时必须要考虑的一个关键点。如果对幂等没有额外的考虑,那么在业务出现处理失败的情况时,可能出现重复消费相同的消息,从而导致出现不符合业务预期的情况。为了避免上述异常,消息队列的消费者在接收到消息后,有必要根据业务上的唯一 Key 对消息做幂等处理。

什么是消息幂等

定义

当业务多次消费到同一条消息的结果与消费一次的结果是相同的,同时多次消费同一条消息并未对业务系统产生任何负面影响,那么这个消费者的处理过程就是幂等的。

场景示例

举个例子,在银行支付系统的场景下,当消费端消费到扣款消息后,系统将对订单执行扣款操作,扣款金额为1元。如果因由于网络不稳定等一些原因导致扣款消息再次被消费到,如果最终的业务结果是只扣款一次,扣费1元,且用户的扣款记录中对应的订单只有一条扣款流水,并没有多次被多次扣费,那么这次扣款操作是符合要求的,整个消费过程实现了消费幂等。

适用场景

发送消息引起消息重复场景

生产者在发送一条消息后,服务端接收成功并完成持久化,如果此时出现了网络闪断或者客户端重启等异常导致服务端对客户端应答失败,此时生产者由于没有收到服务端的确认消息,从而尝试再次发送消息,这时会导致后续消费者会收到两条内容相同但 Message ID 不同的消息。

消费消息引起消息重复场景

消费端消费到了消息并完成业务处理,当消费端给服务端返回 ACK 的时候,此时网络发生异常。当消费端再次来消费消息时,会再次消费到已被处理过的消息,消费者收到了两条内容相同并且 Message ID 也相同的消息。

处理方案

根据以上两种场景可以看出消息重复会出现以下两种情况:
可能出现不同的 Message ID 对应的消息内容相同。
可能是相同的 Message ID 同时消息内容相同。
所以不建议以 Message ID 作为处理依据,建议以业务的唯一标识作为幂等处理的依据。例如支付场景可以将订单号,作为幂等处理的依据。消费到消息后,取出业务中的订单号,业务根据订单号进行判断是否被处理。

代码示例

public static class Order {
public String orderId;
public String orderData;
}
生产端
Producer<Order> producer = client.newProducer(Schema.AVRO(Order.class)).create();
producer.newMessage().value(new Order("orderid-12345678", "orderData")).send();
消费端
Consumer<Order> consumer = client.newConsumer(Schema.AVRO(Order.class)).subscribe();
Order order = consumer.receive().getValue();
String key = order.orderId;
获取到业务的唯一标识 orderId 后,对其进行去重操作。

常见的去重方式

利用数据库进行去重

业务上的幂等操作可以添加一个过滤的数据库,例如设置一个去重表,也可以在数据库中通过唯一索引来去重。
举一个例子,现在要根据订单流转的消息在数据库中写一张订单 Log 表,可以把订单 ID 和修改时间戳做一个唯一索引进行约束。
当消费端消费消息出现相同内容的消息,会多次去订单 Log 表中进行写入,由于添加了唯一索引,除了第一条之外,后面的都会失败,这就从业务上保证了幂等,即使消费多次,也不会影响最终的数据结果。

设置全局唯一消息 ID 或者任务 ID

调用链 ID 也可以应用在这里。在消息生产的时候向每条消息中添加一个唯一 ID,消息被消费后,在缓存中设置一个 Key 为对应的唯一 ID,代表数据已经被消费,当消费端去消费时,就可以根据这条记录,来判断是否已经处理过。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈