You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用队列循环调用函数时的序列号生成异常问题

解决MongoDB并发场景下顺序编号重复问题

问题根源

你遇到的重复编号问题,本质是并发竞态条件:多个队列任务同时读取totalCountOfSavedDocs,此时它们拿到的是同一数值,各自加1后写入数据库,最终导致多个文档使用相同编号。

可行解决方案

1. 使用MongoDB原子自增序列(推荐)

创建专门的序列管理集合,通过MongoDB的原子操作获取唯一递增编号,彻底避免竞态问题。

步骤:

  • 首先创建counters集合,初始化序列文档:
    db.counters.insertOne({ _id: "orderNumber", sequence_value: 0 });
    
  • 在createOrder方法中,替换原有的编号生成逻辑,改用原子操作获取自增编号:
    // 获取并递增订单编号(原子操作)
    const counter = await db.collection('counters').findOneAndUpdate(
      { _id: "orderNumber" },
      { $inc: { sequence_value: 1 } }, // 原子递增
      { upsert: true, returnDocument: "after" } // 不存在则创建,返回更新后的文档
    );
    // 生成的订单编号
    const orderNumber = counter.value.sequence_value;
    
    // 后续使用orderNumber保存订单文档
    
  • 原理:findOneAndUpdate是MongoDB的原子操作,执行时会锁定目标文档,直到操作完成。即使多个并发请求同时触发,也会依次获取递增的编号,不会重复。

2. 添加唯一索引兜底

为订单集合的编号字段添加唯一索引,即使极端情况下出现编号重复(比如代码逻辑疏漏),数据库层面会直接阻止重复数据写入,避免脏数据。

执行以下命令创建索引:

db.orders.createIndex({ orderNumber: 1 }, { unique: true });

在代码中捕获E11000重复键错误,可选择重试编号生成逻辑。

3. 串行化队列处理(低吞吐量场景可选)

如果业务对处理效率要求不高,可将队列改为串行处理模式,确保同一时间只有一个任务生成编号。比如使用Service Bus的**会话(Session)**功能,将所有订单消息分配到同一个会话中,队列会自动串行处理该会话内的消息。

修改消息发送代码,添加会话ID:

for await (let order of orders) {
  const msg: ServiceBusMessage = {
    body: JSON.stringify({
      type: "order-create-single",
      data: order,
      req: reqObj,
    }),
    sessionId: "order-sequence-session" // 指定同一会话ID
  };

  await sender.sendMessages(msg); 
}

注意:这种方式会降低并发处理能力,仅适合订单量较小的场景。

总结

优先选择原子自增序列+唯一索引的组合方案,既保证编号的唯一性与顺序性,又能维持队列的并发处理效率。

内容的提问来源于stack exchange,提问作者Vinayak Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 20:25:02