微服务多表插入场景下重复创建问题的解决方案咨询
解决库存服务队列重试导致的重复插入问题
Awesome question—this is a super common pain point when dealing with queue-driven async operations and multi-table data persistence. Let’s break down some better solutions than the two you’ve tried, plus clarify why your existing approaches have gaps.
1. 优先用本地事务(最简单的方案,若数据库允许)
如果stocks和StocksadditionalInfo在同一个数据库实例里,这是最直接的修复方式。把主表插入和副表插入包裹在本地数据库事务中:
BEGIN TRANSACTION; INSERT INTO stocks (col1, col2, ...) VALUES (...); SET @stock_id = 199282; INSERT INTO StocksadditionalInfo (stock_id, colA, colB, ...) VALUES (@stock_id, ...); COMMIT;
这样两个操作要么都成功,要么都回滚。队列重试时,因为主表数据根本没写入,就不会出现重复插入的问题。这比任何复杂的补偿逻辑都靠谱,前提是两张表在同一个库。
2. 幂等性设计+补偿逻辑(跨库/无法用事务的场景)
如果必须跨库或者不能用本地事务,核心是让你的库存创建操作幂等(不管调用多少次,结果都一致),配合唯一约束和补偿步骤:
- 第一步:给
stocks表新增一个唯一业务标识字段,比如request_uuid(每个创建请求的唯一ID,由前端或网关生成),并添加唯一索引:ALTER TABLE stocks ADD COLUMN request_uuid VARCHAR(36) UNIQUE NOT NULL; - 第二步:处理队列消息时执行以下逻辑:
- 尝试插入主表
stocks,带上request_uuid。 - 如果插入成功(首次处理):继续插入副表;副表插入成功则流程结束。
- 如果插入主表触发
Duplicate entry异常(已处理过主表):- 查询副表
StocksadditionalInfo是否存在对应stock_id的记录。 - 若副表不存在:重新执行副表插入操作。
- 若副表存在:直接返回成功,跳过所有操作。
- 查询副表
- 尝试插入主表
- 这种方式既避免了主表重复插入,又保证了副表数据最终能补全。
3. 状态机+最终一致性(多副表/复杂场景)
如果你的业务有大量副表或者需要精细的状态管控,可以给stocks表加一个status字段(比如INITIAL, PARTIAL, COMPLETED, FAILED):
- 流程:
- 插入主表时,设置
status = INITIAL,同时带上request_uuid。 - 依次插入所有副表,每成功一个更新状态(比如
PARTIAL),全部成功后设置status = COMPLETED。 - 队列重试时,先根据
request_uuid查询主表状态:COMPLETED:直接返回成功。INITIAL/PARTIAL:从失败的步骤开始补全副表数据。- 不存在:执行完整的主表+副表插入流程。
- 插入主表时,设置
- 这种方式适合副表数量多的场景,还能清晰追踪每个库存的创建状态,方便排查问题。
对现有思路的点评
- 先查询再插入/更新:存在竞态条件(比如两个重试请求同时查询,都认为数据不存在,然后同时插入主表),必须配合唯一约束才能解决,单纯查询不是原子操作,所以这个思路不完整。
- 副表单独接口+队列:扩展性太差,随着副表增加,队列和接口会越来越多,维护成本爆炸,完全不推荐。
内容的提问来源于stack exchange,提问作者Ashish Sharma
相关产品推荐
相关产品推荐

