MongoDB 最容易被误解的地方之一,就是事务。
先记住一句最重要的话:
单文档操作是原子的。
也就是说,如果你的业务能被设计成“一个关键动作只改一份档案袋”,MongoDB 会非常自然。
比如更新订单文档中的状态、支付时间、物流号:
db.orders.updateOne(
{ orderNo: "ORD-9001" },
{
$set: {
status: "shipped",
shippedAt: new Date(),
trackingNo: "SF12345678"
}
}
)这一次更新,对这份订单文档来说就是原子的。
那什么时候需要事务?
- 同时改多份文档
- 同时改多个集合
- 中间任何一步失败都必须整体回滚
例如转账:
const session = db.getMongo().startSession()
session.withTransaction(() => {
const accounts = session.getDatabase("bank_archive").accounts
accounts.updateOne(
{ _id: "A", balance: { $gte: 100 } },
{ $inc: { balance: -100 } }
)
accounts.updateOne(
{ _id: "B" },
{ $inc: { balance: 100 } }
)
})但这里的关键认知不是“MongoDB 也能做事务”,而是:
有事务,不代表应该先靠事务救设计。
更推荐的思路是:
- 先把模型设计成尽量利用单文档原子性
- 只有确实跨文档一致性不可避免时,再启用事务
在弹性档案仓里,最优方案通常是:
尽量让一个业务动作,落在尽量少的档案袋上。