PAYATHON 2026

MongoDB 使用 $expr 和 $push 限制数组长度时是否原子?

数据小陈

结论

在 members 始终为数组的前提下,这个 updateOne 能在并发写入时将数组长度限制在 3 以内。MongoDB 对单个文档的更新是原子的,条件匹配和 $push 会作为同一次文档更新执行。每次并发更新都会根据执行时文档的当前状态重新判断条件,不会都依据同一份旧数组长度成功。

多个客户端同时尝试添加元素时,成功的更新会增加数组长度。长度达到 3 后,后续更新将不再匹配,因此这些更新不会让数组超过 3 个元素。

更新示例

db.collection.updateOne(
  {
    _id: docId,
    $expr: { $lt: [{ $size: "$members" }, 3] }
  },
  {
    $push: { members: newMember }
  }
)

可以通过 matchedCount 检查是否有文档满足条件。结果为 0 时,可能是文档不存在,也可能是数组长度已达到上限。如果业务需要区分这两种情况,应另行查询或处理。

使用前提和注意事项

  • members 必须存在且为数组。查询表达式中的 $size 遇到字段缺失或字段不是数组时会导致更新报错。可以通过数据校验保证字段类型,也可以在条件中处理字段缺失和类型问题。
  • 这个条件只限制通过该更新路径添加元素。其他写入路径如果不检查长度,仍可能让数组超过 3 个元素。所有相关写入都应遵守同一约束,也可以考虑使用集合校验规则,避免写入不合规数据。
  • 如果业务要求并发失败的客户端重试,应重新执行带长度条件的更新,并处理数组已达上限、更新仍未匹配的情况。
  • $push 配合 $slice 可以截断数组,但超出范围的元素会被丢弃。达到上限后拒绝新增元素的语义与此不同。

备注:内容仅供参考。