必须手动创建counters集合并统一字段名为seq_value,用findOneAndUpdate替代已弃用的findAndModify;高并发下需注意单文档热点问题;MongoDB 6.0+的autoIncrement仅限普通字段且为实验性功能。

counters 集合必须手动创建且字段名要统一
不能依赖 MongoDB 自动建集合,counters 必须显式创建:db.createCollection("counters")。否则 findOneAndUpdate 在 upsert: true 下虽能插入文档,但集合缺失会导致部分驱动(如 Java Spring Data)抛出 NamespaceNotFoundException。字段名推荐用 seq_value 或 sequence_value,避免混用 seq / value / nextId —— 多个服务共用同一 counters 时,字段不一致会直接导致 undefined 返回。
findAndModify 已弃用,必须改用 findOneAndUpdate
MongoDB 5.0 起 findAndModify 在 shell 中仍可用,但官方明确标记为 deprecated;6.0+ 版本中,所有新驱动(Node.js 6.x+、Python PyMongo 4.0+、Java MongoDB Driver 4.11+)已移除该方法支持。继续用会报错:TypeError: db.counters.findAndModify is not a function。正确写法是:
db.counters.findOneAndUpdate(
{ _id: "orders" },
{ $inc: { seq_value: 1 } },
{ returnDocument: "after", upsert: true }
)
注意三个关键点:
-
returnDocument: "after"(不是new: true) -
upsert: true必须显式声明,否则首次调用会返回null - 回调或 await 后需判空:
if (!result) throw new Error("counter init failed")
并发安全靠原子性,但高吞吐下仍可能成瓶颈
$inc 操作本身是单文档原子的,counters 方案在中小流量下完全可靠。但要注意:
- 所有自增请求都争抢同一个
counters文档(比如_id: "users"),在每秒上千次写入时,该文档会成为热点,引发 write lock 等待 - 分片集群中,
counters必须放在未分片集合(即单一分片上),否则跨分片事务不支持upsert + $inc原子组合 - 如果业务允许“非严格连续”,可考虑预分配段(如一次取 100 个号缓存在内存),减少 DB 调用频次
6.0+ 原生 autoIncrement 是更优替代,但仅限字段级
MongoDB 6.0 引入了实验性 autoIncrement 功能,通过 collMod 命令启用,例如:
db.runCommand({
collMod: "orders",
autoIncrement: {
field: "order_no",
start: 100001,
step: 1
}
})
它比 counters 更轻量,无需额外集合和手动调用逻辑。但限制明显:
- 只支持普通字段(如
order_no),不能用于_id字段 - 不支持多字段共用同一序列(比如
user_id和product_id不能共享起点) - 目前仍是
experimental功能,生产环境启用前需确认副本集版本和兼容性矩阵
真正需要 _id 自增,或跨集合复用序列,counters + findOneAndUpdate 仍是唯一稳妥路径 —— 只是别再写 findAndModify 了。


















