Skip to content

自增序列

MongoDB 的默认 _id 是一个 12 字节的 ObjectId,这对于分布式系统中的唯一性非常理想。然而,一些应用程序需要一个简单、人类可读的自动递增整数序列(例如 1, 2, 3…)。MongoDB 并没有开箱即用地提供此功能,但您可以使用对专门的“计数器”集合执行原子操作来实现。

在实现之前,请考虑其权衡:虽然简单,但此方法每次插入都需要额外的一次数据库调用,如果设计不当,可能成为分片集群上的写入瓶颈。在大多数情况下,默认的 ObjectId 是更好的选择。

该策略涉及两个步骤:

  1. 创建一个 counters 集合,用于存储您希望自动递增的每个键的最新序列值。
  2. 在您的应用程序中创建一个函数,以原子方式查找并递增计数器,然后返回新值。

首先,在 MongoDB Shell (mongosh) 中,插入一个文档来跟踪我们的 productId 序列。我们将从 0 开始序列。

// Connect to your database
use my_app_db;
// Insert the initial counter document
db.counters.insertOne({
_id: "productId",
sequence_value: 0
});

创建应用程序端函数 (Node.js 示例)

Section titled “创建应用程序端函数 (Node.js 示例)”

您应该在应用程序代码中实现此逻辑,而不是在数据库内部创建函数(一种传统做法)。这样做更易于维护、更具可伸缩性且更安全。这是一个使用 Node.js 和官方 mongodb 驱动程序的示例。

const { MongoClient } = require('mongodb');
// Replace with your connection string
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
/**
* Gets the next value in a sequence.
* @param {Db} db The database instance.
* @param {string} sequenceName The name of the sequence (e.g., 'productId').
* @returns {Promise<number>} The next sequence value.
*/
async function getNextSequenceValue(db, sequenceName) {
const sequenceDocument = await db.collection('counters').findOneAndUpdate(
{ _id: sequenceName },
{ $inc: { sequence_value: 1 } },
{ returnDocument: 'after' } // Ensures the updated document is returned
);
if (!sequenceDocument) {
throw new Error(`Sequence '${sequenceName}' not found. Please initialize it.`);
}
return sequenceDocument.sequence_value;
}
// --- Main Application Logic ---
async function main() {
try {
await client.connect();
const db = client.db('my_app_db');
const productsCollection = db.collection('products');
// Create a new product using the sequence
const newProduct = {
_id: await getNextSequenceValue(db, 'productId'),
product_name: 'Super High-Tech Widget',
category: 'electronics'
};
const result = await productsCollection.insertOne(newProduct);
console.log(`Successfully inserted new product with _id: ${result.insertedId}`);
// Create another one
const anotherProduct = {
_id: await getNextSequenceValue(db, 'productId'),
product_name: 'Another Cool Gadget',
category: 'electronics'
};
const anotherResult = await productsCollection.insertOne(anotherProduct);
console.log(`Successfully inserted new product with _id: ${anotherResult.insertedId}`);
} catch (e) {
console.error('An error occurred:', e);
} finally {
await client.close();
}
}
main();

这种现代方法使用 findOneAndUpdate,它是一个原子操作,确保即使在并发请求下,每次调用也会获得一个唯一的、递增的值。returnDocument: 'after' 选项对于获取新值至关重要。