自增序列
MongoDB - 自动递增序列
Section titled “MongoDB - 自动递增序列”MongoDB 的默认 _id 是一个 12 字节的 ObjectId,这对于分布式系统中的唯一性非常理想。然而,一些应用程序需要一个简单、人类可读的自动递增整数序列(例如 1, 2, 3…)。MongoDB 并没有开箱即用地提供此功能,但您可以使用对专门的“计数器”集合执行原子操作来实现。
在实现之前,请考虑其权衡:虽然简单,但此方法每次插入都需要额外的一次数据库调用,如果设计不当,可能成为分片集群上的写入瓶颈。在大多数情况下,默认的 ObjectId 是更好的选择。
计数器集合模式
Section titled “计数器集合模式”该策略涉及两个步骤:
- 创建一个
counters集合,用于存储您希望自动递增的每个键的最新序列值。 - 在您的应用程序中创建一个函数,以原子方式查找并递增计数器,然后返回新值。
步骤 1:初始化计数器集合
Section titled “步骤 1:初始化计数器集合”首先,在 MongoDB Shell (mongosh) 中,插入一个文档来跟踪我们的 productId 序列。我们将从 0 开始序列。
// Connect to your databaseuse my_app_db;
// Insert the initial counter documentdb.counters.insertOne({ _id: "productId", sequence_value: 0});创建应用程序端函数 (Node.js 示例)
Section titled “创建应用程序端函数 (Node.js 示例)”您应该在应用程序代码中实现此逻辑,而不是在数据库内部创建函数(一种传统做法)。这样做更易于维护、更具可伸缩性且更安全。这是一个使用 Node.js 和官方 mongodb 驱动程序的示例。
const { MongoClient } = require('mongodb');
// Replace with your connection stringconst 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' 选项对于获取新值至关重要。