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

MongoDB如何实现插入后自动递增的ID来访问记录?

实现MongoDB中自动递增ID的正确方案

你之前考虑的查询当前最大id再递增的方案存在两个核心问题:一是并发场景下会出现重复ID(多个请求同时查询到相同的最大值,导致插入重复id);二是如果删除过文档,会复用已删除的id(如果这不符合你的业务需求)。

MongoDB官方推荐使用计数器集合的方式实现可靠的自动递增ID,通过原子操作避免并发问题,同时不受文档删除的影响。具体实现步骤如下:

1. 创建计数器集合的模型

首先定义一个专门存储各集合递增序列的计数器模型,用来记录每个集合当前的最大ID值:

const mongoose = require('mongoose');

const counterSchema = new mongoose.Schema({
  collectionName: { 
    type: String, 
    required: true, 
    unique: true // 确保每个集合对应唯一的计数器
  },
  sequenceValue: { 
    type: Number, 
    default: 0 // 初始值设为0,第一次插入会递增到1
  }
});

module.exports = mongoose.model('Counter', counterSchema);

2. 修改人员集合的Schema,添加自动递增逻辑

在人员的Schema中使用pre('save')中间件,在文档保存前通过原子操作获取下一个递增ID:

const mongoose = require('mongoose');
const Counter = require('./counter.model'); // 引入计数器模型

const personSchema = new mongoose.Schema({
  id: { 
    type: Number, 
    unique: true // 唯一约束,作为重复ID的最后防线
  },
  name: String,
  role: String
});

// 保存前自动生成递增ID
personSchema.pre('save', async function(next) {
  const doc = this;
  // 仅在新增文档(未设置id)时生成ID
  if (!doc.id) {
    // 原子操作:找到对应集合的计数器并递增序列值
    const counter = await Counter.findOneAndUpdate(
      { collectionName: 'Person' }, // 对应人员集合的名称
      { $inc: { sequenceValue: 1 } }, // 原子递增操作
      { new: true, upsert: true } // upsert:不存在则创建;new:返回更新后的计数器
    );
    doc.id = counter.sequenceValue;
  }
  next();
});

module.exports = mongoose.model('Person', personSchema);

方案优势

  • 原子性保障:findOneAndUpdate是MongoDB的原子操作,即使多个请求同时触发,也只会有一个请求能成功递增序列值,彻底避免并发重复ID问题。
  • 不受文档删除影响:计数器独立存储,人员集合中的文档被删除后,计数器的序列值不会回退,因此不会出现复用已删除ID的情况(符合大多数业务对递增ID的要求)。

注意事项

  • 确保计数器集合的collectionName与实际集合名称一一对应,避免不同集合共用计数器。
  • 批量插入文档时,pre('save')会逐个处理文档,若想优化性能,可以一次性获取多个递增ID,减少数据库交互次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:33:27