Skip to content

一致性与事务

MongoDB 的一致性不是单一开关,而是由单文档原子性、Write Concern、Read Concern、Read Preference、Session 和事务共同决定。只说“MongoDB 支持事务”无法说明一次读写实际获得的保证。


单文档原子性

对单个文档的写操作是原子的,即使一次更新修改多个嵌套 Field。把必须共同变化的数据放进同一文档,可以不启用多文档事务:

javascript
db.accounts.updateOne(
  { _id: accountId, balance: { $gte: 100 } },
  { $inc: { balance: -100 }, $push: { entries: entry } }
)

Filter 同时承担并发前置条件。应用必须检查 matchedCountmodifiedCount,不能只根据请求没有抛异常就认定业务更新成功。


Write Concern

Write Concern 定义写操作需要获得什么级别的确认:

设置含义风险与成本
w: 1Primary 确认接收写入Primary 故障时尚未复制的写可能回滚
w: "majority"多数投票成员确认写入延迟受多数派网络和磁盘影响
j: true等待相关节点的 Journal 持久化提升节点崩溃后的持久性,增加确认延迟
wtimeout限制等待确认时间超时表示确认条件未满足,不必然代表写入未发生

客户端超时或网络断开后,写入状态可能未知。可重试写、幂等键和业务去重用于处理“请求结果未知”,不能简单地无条件再次插入。


Read Concern 与 Read Preference

Read Concern 决定读到什么一致性级别;Read Preference 决定去哪个复制集成员读取。二者不能互相替代。

Read Concern主要语义
local返回当前节点可见的数据,故障切换后可能回滚
majority返回多数派已提交的数据
linearizable对 Primary 上单文档读取提供更强实时顺序保证,限制和成本较高
snapshot事务或受支持场景中的一致时间点视图

从 Secondary 读取可能降低 Primary 读压力或改善地域延迟,但数据可能落后。关键业务的“刚写完立即读到”需要结合 Session、因果一致性、Read Preference 与 Concern 明确设计。


多文档事务

javascript
const session = db.getMongo().startSession()
const accounts = session.getDatabase("bank").accounts

session.startTransaction({
  readConcern: { level: "snapshot" },
  writeConcern: { w: "majority" }
})

try {
  accounts.updateOne({ _id: from }, { $inc: { balance: -100 } })
  accounts.updateOne({ _id: to }, { $inc: { balance: 100 } })
  session.commitTransaction()
} catch (error) {
  session.abortTransaction()
  throw error
} finally {
  session.endSession()
}

事务具有超时、锁、Cache、Oplog 和重试成本。驱动通常提供带重试规则的 Transaction Callback API,生产代码应按驱动规范处理 Transient Transaction Error 和 Unknown Commit Result。

多文档事务适合无法通过聚合边界消除的少量强一致变更,不应成为照搬关系模型的默认工具。跨 Shard 事务还会引入分布式协调。

参考资料