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可以截断数组,但超出范围的元素会被丢弃。达到上限后拒绝新增元素的语义与此不同。
备注:内容仅供参考。
