MongoDB Academy · 课时

分布式文档存储中的 ACID 保证

您将把 ACID 的四项属性与 MongoDB 的存储引擎联系起来,并了解单文档级别默认提供哪些保证。

第 1 / 4 课13 个步骤

分布式文档存储中的 ACID 保证 是 CoddyKit 上的免费 MongoDB Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 MongoDB Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 MongoDB Academy 课程共包含 4 节课。

ACID 对数据库意味着什么

ACID 代表原子性、一致性、隔离性和持久性——这四种属性共同保证数据库操作能够可靠地处理。这些属性最初是为传统关系数据库定义的,但对于文档存储同样重要。了解 MongoDB 如何提供(或权衡)每种属性,有助于您设计出满足应用程序可靠性要求的数据模型和操作。

原子性:要么全部成功,要么全部失败

原子性保证一组操作要么全部成功,要么全部失败——不会留下部分状态。在 MongoDB 中,单文档操作始终具有原子性。当您使用多个更新运算符调用 updateOne 时,整个变更会作为一个原子单元应用。这是因为一个文档的所有数据通常都会以 BSON 格式一起存储在磁盘上。

// This entire updateOne is atomic — both fields change together or neither does
db.accounts.updateOne(
  { _id: accountId },
  {
    $inc: { balance: -100 },
    $push: { transactions: { type: 'debit', amount: 100, date: new Date() } }
  }
)

单文档原子性与多文档原子性

MongoDB 默认在单文档级别保证原子性。由于一个文档可以包含嵌入式数组和嵌套对象,您通常可以将原本需要多个 SQL 行表示的数据建模为一个文档,从而免费获得原子更新。多文档原子性需要显式使用多文档事务(副本集从 MongoDB 4.0 起可用)。了解这一差异有助于指导您的数据建模决策。

// No transaction needed: order + line items in one document = atomic
db.orders.insertOne({
  _id: orderId,
  customerId: customerId,
  status: 'pending',
  items: [
    { productId: 'P1', qty: 2, price: 29.99 },
    { productId: 'P2', qty: 1, price: 49.99 }
  ],
  total: 109.97
})

一致性:有效的状态转换

一致性意味着数据库始终从一个有效状态转换到另一个有效状态。在 MongoDB 中,一致性通过JSON Schema 验证器(字段类型、必填字段、枚举值)、唯一索引(不允许重复值)以及应用程序级不变量来保证。与传统 RDBMS 不同,MongoDB 不会原生强制执行外键——必须由应用程序代码或模式设计来维护引用完整性。

// JSON Schema validator enforces consistency constraints
db.createCollection('users', {
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['email', 'role'],
      properties: {
        email: { bsonType: 'string' },
        role: { enum: ['admin', 'user', 'guest'] }
      }
    }
  }
})

隔离性:并发操作的行为

隔离性控制并发操作如何查看彼此的变更。MongoDB 对多文档事务使用快照隔离:事务会看到事务开始时数据的一致快照。在事务之外,单独的读取可能会立即看到其他操作已提交的变更——这称为读已提交隔离。副本集上的读取偏好会影响您读取的快照来源。

// Inside a transaction, a consistent snapshot is maintained
const session = client.startSession();
session.startTransaction();
try {
  // These two reads see the SAME snapshot even if other writers commit between them
  const inventory = await db.collection('inventory').findOne({ _id: itemId }, { session });
  const order = await db.collection('orders').findOne({ _id: orderId }, { session });
  // ...
  await session.commitTransaction();
} finally {
  await session.endSession();
}

持久性:经受故障

持久性保证操作一旦得到确认,即使系统崩溃,操作结果也会保留。MongoDB 通过 WiredTiger 日志实现持久性——写入会先记录到日志中,然后再应用到数据文件。writeConcern 选项可以让您控制持久性级别:w:1 在一个节点完成写入后确认,w:majority 则等待副本集成员中的大多数持久化该写入。

// w:majority ensures write survives even if the primary fails
db.payments.insertOne(
  { orderId: orderId, amount: 99.99, status: 'completed' },
  { writeConcern: { w: 'majority', j: true } }
  // j:true = wait for journal flush on disk
)

单文档写入始终具有持久性

对于使用默认写关注的副本集上的单文档操作,MongoDB 会在主节点确认写入后才响应。如果操作指定了 j:true,MongoDB 还会等待日志刷新到磁盘。这意味着单文档写入可以抵御主节点崩溃(副本集故障转移)和磁盘故障(日志可确保重启时不会丢失数据)。

// Default write concern on Atlas: {w: 'majority'} — already durable
// Explicitly requesting journal flush:
await db.collection('criticalAuditLog').insertOne(
  { event: 'payment', userId: userId, timestamp: new Date(), amount: 500 },
  { writeConcern: { w: 'majority', j: true } }
);

嵌入式文档对 ACID 的优势

MongoDB 的一个关键设计理念是,将相关数据嵌入单个文档,可以在许多情况下消除对多文档事务的需求。包含其行项目的订单、包含其评论的博客文章、包含其地址的用户资料——这些都是单个文档,因此可以免费获得原子、一致、隔离且持久的更新,而无需承担事务的开销。

// Updating shipping address + logging the change:
// One atomic write — no transaction needed
await db.collection('users').updateOne(
  { _id: userId },
  {
    $set: { 'address.street': '123 Main St', 'address.city': 'Austin' },
    $push: {
      addressHistory: {
        changedAt: new Date(),
        previous: oldAddress
      }
    }
  }
)

何时需要多文档 ACID

在某些情况下,嵌入并不可行,因此需要多文档 ACID。两个独立账户文档之间的资金转账(从一个账户扣款、向另一个账户入账)要求跨两个文档实现原子性。库存预留(在一个集合中减少库存、在另一个集合中创建订单)需要隔离性。跨多条记录的分布式账本更新需要要么全部成功、要么全部失败的保证。对于这些模式,MongoDB 4.0 及更高版本的多文档事务就是解决方案。

// Without a transaction, a crash between these two writes
// leaves the database in an inconsistent state (money debited but not credited):
await db.collection('accounts').updateOne({ _id: fromId }, { $inc: { balance: -100 } });
// <--- system crash here means money is lost!
await db.collection('accounts').updateOne({ _id: toId }, { $inc: { balance: 100 } });

// With a transaction, both succeed or both roll back.

ACID 与 BASE:一个连续谱

并非所有 NoSQL 数据库都提供 ACID 保证。许多早期 NoSQL 系统选择了 BASE(基本可用、软状态、最终一致),以便在分布式节点之间获得更高的写入吞吐量和可用性。MongoDB 的定位是:默认在文档级别提供 ACID,并可按需为多文档事务提供完整 ACID——这介于严格的 RDBMS 和完全采用最终一致性的存储之间。

读己之写:因果一致性

在分布式副本集中,主节点上的写入和随后从从节点发起的读取可能暂时看不到该写入——这是一种一致性异常。MongoDB 的因果一致性功能(可通过会话使用)保证同一会话中的操作能够看到该会话中所有先前操作的影响,即使这些操作发生在不同服务器上。这对于写入后的应用程序正确行为至关重要。

// Causal consistency: guaranteed to read your own writes within a session
const session = client.startSession({ causalConsistency: true });
await db.collection('settings').updateOne(
  { _id: userId }, { $set: { theme: 'dark' } }, { session }
);
// This read is guaranteed to see the update above, even on a secondary:
const settings = await db.collection('settings').findOne({ _id: userId }, { session });
await session.endSession();

快速检查

测试您对本课中 MongoDB 和 NoSQL 数据库相关概念的理解。

课程回顾

在本课中,您学习了:MongoDB 中所有单文档操作默认都完全符合 ACID;文档级别的原子性在许多情况下消除了对事务的需求;以及多文档 ACID 事务(MongoDB 4.0 及更高版本)可以处理嵌入并不实用的情况,例如在独立文档之间进行资金转账。接下来,我们将探索如何打开会话并编写多文档事务。

免费开始

用 AI 导师学习 JavaScript — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「分布式文档存储中的 ACID 保证」课时是免费的吗?

是的 — 「分布式文档存储中的 ACID 保证」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 MongoDB Academy 课程的其余内容,请升级到 CoddyKit PRO。 MongoDB Academy 课程共包含 4 节课。

「分布式文档存储中的 ACID 保证」这节课中我会学到什么?

您将把 ACID 的四项属性与 MongoDB 的存储引擎联系起来,并了解单文档级别默认提供哪些保证。 你通过在浏览器中直接运行的动手代码来练习 MongoDB Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 MongoDB Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 MongoDB Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「分布式文档存储中的 ACID 保证」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 MongoDB Academy 课中编写并运行代码吗?

能。每节 MongoDB Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 分布式文档存储中的 ACID 保证
  2. 启动会话和多文档事务
  3. 错误处理和重试逻辑
  4. 事务性能考量
← 返回 MongoDB Academy