HuangNobody / advanced-java

😮 Core Interview Questions & Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识
https://doocs.github.io/advanced-java
Creative Commons Attribution Share Alike 4.0 International
0 stars 0 forks source link

保证消息队列 幂等性 #1

Open HuangNobody opened 14 hours ago

HuangNobody commented 14 hours ago

如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?

文章中说到,保证消息幂等性:“比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。”

但是,如果主键是插入时自动生成的id,就没办法使用主键先来查一下吧?那数据库怎么实现幂等性呢?

HuangNobody commented 14 hours ago

在主键是自动生成的情况下,无法用它直接查重。要实现消息幂等性,可以采用其他方法来确保唯一性,比如使用业务上独特且不变的字段(或字段组合)来进行查找。以下是几种常见的解决方案:

使用业务唯一键:为需要幂等处理的表增加一个具有业务唯一性的字段,例如订单号、用户ID+日期、业务流水号等。在写入数据前,先根据该唯一字段来查找记录。如果已存在,则执行 UPDATE 或忽略操作,如果不存在则执行 INSERT。这种方法适合业务场景中有明确唯一性的字段时使用。

借助唯一索引:可以为业务上需要保证唯一的字段设置唯一索引。写入时直接执行 INSERT,如果该字段已存在,数据库会抛出唯一索引冲突错误。可以捕获此错误并将操作转换为 UPDATE。例如:

sql 复制代码 INSERT INTO your_table (unique_field, data_field) VALUES ('unique_value', 'data_value') ON DUPLICATE KEY UPDATE data_field = 'data_value'; 这样即便多次插入相同的 unique_field,数据库也只会执行一次插入,其余会变成更新操作。

使用状态表:维护一个幂等控制表,记录已处理过的消息ID或请求ID。每次处理前先检查该表是否已有此ID,若已存在则跳过处理或更新状态。如果没有,则记录ID并继续正常处理。这种方式适合处理跨系统的消息幂等。

分布式锁:对于分布式环境,可以使用分布式锁(例如 Redis 锁, 可以通过 Redis 的原子操作 SETNX(set if not exists)结合过期时间来实现分布式锁。 )在操作前锁定特定的业务字段,确保只有一个操作成功插入数据,其他操作在解锁后再检查插入结果,确保幂等性。