数据库管理系统 ACID 属性指南
随着实时应用程序和物联网 (IoT) 设备的使用,加上非结构化数据资产的指数级增长,越来越多的组织正在开始采用 NoSQL 数据库。事实上,NoSQL 市场预计到 2032 年将达到 745 亿美元,从 2024 年到 2032 年的增长率(CAGR)为 24.9%(IMARC 集团,2024 年)。
鉴于 NoSQL 数据库管理系统 (DBMS) 能够有效管理大型多样化数据集以及所需的相关大数据分析,这种增长并不令人惊讶。
然而,许多人仍然认为 NoSQL DBMS 无法解决许多组织的关键需求 — ACID 事务管理和合规。好消息是,某些 NoSQL DBMS 具备这种能力!
在本文中,我们将讨论什么是 ACID 事务、ACID 事务的属性、ACID 事务为何重要以及 NoSQL DBMS 中的 ACID 事务示例。
目录
- ACID 事务简史
- 什么是ACID事务?
- ACID 事务为何重要?
- DBMS 中的 ACID 事务示例
- ACID 事务在 MongoDB 中的运行机制?
- 我应在哪些情况下使用 MongoDB 多文档事务?
- MongoDB 中的事务最佳实践是什么?
- 常见问题解答
ACID 事务简史
自 20 世纪 70 年代末以来,ACID 事务的概念一直是现代数据库系统发展的核心。
随着企业与科研机构对数字数据的依赖程度不断加深,对跨多记录复杂运算的可靠处理方案的需求日益凸显。IBM System R、Oracle 等早期关系型数据库,是首批尝试强化数据完整性与事务可靠性更严格保障机制的产品。
1983 年,计算机科学家 Andreas Reuter 和 Theo Härder 在一篇具有里程碑意义的论文中正式定义了四个 ACID 属性,即原子性、一致性、隔离性和持久性。他们的工作为数据库社区提供了一种通用语言,用以描述可靠数据库事务的要求。每项属性都旨在解决确保数据即使在发生故障时,也能保持有效和可靠处理的特定挑战:
- 原子性保证事务中的多个操作充当单个单元,要么全部成功,要么全部失败。
- 一致性确保数据库保持在遵循既定规则和约束的有效状态。
- 隔离性可以防止并发事务相互干扰。
- 持久性确保一旦事务提交,结果就是永久性的,即使在系统故障之后也是如此。
该框架成为了关系数据库管理系统(RDBMS)的黄金标准,尤其是在企业将其应用于银行、零售和电信等行业时——在这些行业中,金融事务和监管合规要求绝对的准确性。
在整个 20 世纪 90 年代和 21 世纪初,像 Oracle、IBM DB2 和 Microsoft SQL Server 等关系型数据库继续优化实现 ACID 一致性保证的方式,经常使用预写式日志、锁和多版本并发控制等技术。
这些方法使组织能够在保持严格一致性的同时处理数百万次数据库操作。
随着 2010 年代 NoSQL 数据库的兴起,一些人质疑在为横向扩展和高可用性而设计的系统中能否保留 ACID 一致性保证。早期的 NoSQL 平台通常会为了性能和可用性而放宽一致性,这导致人们认为 ACID compliance 与 NoSQL 可扩展性是相互排斥的。
然而,随着分布式系统对数据完整性的需求不断增长,像 MongoDB 这样的供应商证明,在扩展到多个节点的同时,还可以支持多文档事务。
如今,ACID 事务依然很重要。 它们为在错误不可接受的行业中保持数据完整性奠定了基础,从银行账户转账到医疗记录。
同时,MongoDB 等现代平台已将 ACID 一致性保证扩展到灵活的非结构化数据以及分布式集群中的复杂操作。 这种可靠性和可扩展性的结合,确保了 40 多年前首次定义的 ACID 原则在继续推动数据库管理迈向未来。
什么是ACID事务?
事务详解
从最基本的层面上来说,数据库事务是一组根据 DBMS 定义(例如,定义的事务标准)成功完成的数据库读写操作。事务主要有两种类型:
- 单个事务:单个事务是指一系列一个或多个数据库操作,导致一个动作,成功完成。一旦完成,事务即被接受,并可在事务日志中找到。单个事务的常见例子是从 ATM 机取款。
- 多事务:多事务,有时也称为分布式事务,由分布在不同数据库和系统(例如分布式系统)中的多个相互依赖的事务组成。 这些事务的记录也可以在事务日志中找到。 这些事务的示例包括将资金从一个账户转入另一个账户,或者雇主为新员工发放带有照片的安全徽章。
请务必注意,某些事务需要遵守严格的数据完整性(例如,数据完整且正确)和数据一致性(例如,所有表/数据库中的值相同)标准。这通常包括涉及受托责任或监管合规的情况。
例如商业银行业务、投资经纪业务和法律和解。
在这些情况下,仅仅遵守 DBMS 定义的标准是不够的,还需要符合 ACID 事务要求。
ACID 事务
ACID 是原子性、一致性、隔离性和持久性的缩写。ACID 属性共同确保了一组数据库操作(在事务中组合在一起)即使在出现意外错误时也能使数据库处于有效状态。
此外,ACID 事务提供了许多监管机构要求的 ACID 一致性保证级别。
下面提供了每个 ACID 事务元素的简要概述,以及 NoSQL 文档数据库如何处理相应 ACID 元素的说明。本文将使用 MongoDB Atlas。
原子性(Atomicity)
原子性保证组成事务的所有命令都被视为一个单元,要么一起成功,要么一起失败。这一属性在系统故障或断电时非常重要,因为如果一个事务没有完全处理完成,它就会被舍弃,数据库就能保持数据的完整性。
MongoDB 如何处理原子性
在 MongoDB 中,写入操作在单个文档级别上是原子性的,即使该操作修改单个文档中的多个嵌入文档也是如此。
对于需要对多个文档(在单个集合或多个集合中)的读写操作具有原子性的情况,MongoDB 支持分布式事务,包括副本集和分片集群上的事务。
一致性(Consistency)
一致性保证在 ACID 事务中所做的更改填充到数据库系统(例如节点)中,并与 DBMS 约束保持一致。如果处于不一致状态的 ACID 事务对数据一致性产生负面影响,则整个 ACID 事务将失败。
MongoDB 如何处理一致性
MongoDB 可以灵活地规范化或复制数据,从而优化应用程序。如果模式中存在重复数据,则开发人员必须决定如何在多个集合中保持重复数据的一致性。一些应用程序需要立即使重复的数据保持一致,而其他应用程序可以容忍读取过时数据。示例如下所示:
注意:了解有关数据一致性的更多信息。
隔离性
每个事务都与其他事务隔离,以防止数据冲突。 这还有助于与管理多个条目和多级事务相关的数据库操作。
例如,如果两个用户试图修改相同的数据(甚至是同一个事务),DBMS 会使用一种称为锁管理器的机制来暂停其他用户,直到第一个用户所做的更改完成为止。
MongoDB 如何处理隔离性
MongoDB 采用一种称为快照隔离的技术(例如,每个事务似乎都在事务开始时拍摄的数据库的个人快照上运行)。 事务可以从事务开始时提交的数据的“快照”中读取数据,任何冲突的更新都将导致事务中止。
此外,MongoDB 事务还支持事务级写关注。客户端可以设定适当的读关注和写关注级别,其中最严格的是将快照读关注与大多数写关注点相结合。大多数写关注意味着写入操作已持久性地提交给计算出的大多数数据承载节点(可由开发人员配置)。
持续性(Durability)
持久性保证,一旦事务完成并将更改写入数据库,它们就会持续存在。这样可以确保即使在系统故障(例如崩溃或服务中断)的情况下,系统内的数据也将持久存在。持久性的概念是数据可靠性的关键要素。
MongoDB 如何处理持久性
MongoDB 创建一个 OpLog,其中包含每次“写入”的磁盘位置和更改的字节。如果在写入事务期间发生不可预见的事件(例如停电),则可以在系统重新启动时使用 OpLog 来重放关机前未刷新到磁盘的所有写入操作。
此外,操作在写入 OpLog 之前会被更改,因此它们具有幂等性,可以多次重试。默认情况下,事务或“写入”大约每 60 秒刷新到磁盘一次。
ACID 事务为何重要?
ACID 事务有助于保持数据的完整性和可靠性,同时确保受政府或行业监管的重要数据(如银行账户、股票投资组合)符合所需标准。此外,ACID 合规性通常是在分布式数据库系统中实现数据复制和实现高可用性的先决条件。
DBMS 中的 ACID 事务示例
此示例使用 NoSQL 文档数据库 MongoDB Atlas,演示了如何处理 ACID 多文档事务以及 ACID 事务如何保证与最低 ACID 属性标准一致。
ACID 多文档事务
想象一下,您正在构建一项从一个银行账户向另一个银行账户转账的功能,其中每个账户都有自己的记录。 如果从源账户成功提取了资金,但却从未将其记入目标账户,就会产生严重的会计问题。 相反,如果存入目标账户但从未从源账户中扣款,则会出现另一个严重的会计问题。
这两个写入操作必须同时发生或同时不发生,才能保持系统及其数据的一致。此外,这意味着如果ACID 事务中的任何命令失败,则数据库必须回滚(例如,撤消)其在 ACID 事务过程中写入的所有更改。
注意:要了解详情,请访问 Node.js 快速入门 GitHub 存储库,获取完整代码样本的副本并自行运行。
需要注意的影响
在分布式系统中处理多文档事务时,请记住存在性能开销影响,这可能会影响资源限制和性能目标。此外,由于数据库必须“锁定”相关资源以防止并发写入相互干扰(例如,事务失败),因此尝试写入数据的其他客户端可能会等待事务完成,这可能会影响应用程序延迟和用户体验。
ACID 事务在 MongoDB 中的运行机制?
MongoDB 的文档模型允许将相关数据一起存储在单个文档中。文档模型与原子文档更新相结合,在大多数使用案例中消除了对事务的需要。尽管如此,在某些情况下,真正的多文档、多集合 MongoDB 事务才是最佳选择。
MongoDB 事务的工作方式与其他数据库中的事务类似。要使用 ACID 事务,请通过驱动程序启动 MongoDB 会话。然后,使用该会话执行群组数据库操作。您可以跨多个文档、集合和分片运行任何 CRUD(创建、读取、更新和删除)操作。
有关如何实现事务的特定代码样本,请参阅以下快速入门指南:
- 在 MongoDB 中使用 Golang 实现多文档 ACID 事务
- MongoDB 中使用 Java 进行多文档 ACID 事务
- 结合 Node.js 实现 MongoDB 中的多文档 ACID 事务
- MongoDB 中使用 Python 的多文档 ACID 事务
访问 MongoDB 驱动程序文档,获取 MongoDB 正式支持的每种语言的具体指南。 您还可以查看事务最佳实践和生产注意事项列表。
我应在哪些情况下使用 MongoDB 多文档事务?
需要事务的应用程序通常具有在不同方之间交换值的用例。这些通常是“记录系统”或“业务线”应用程序。
可能从多文档事务中受益的示例应用程序包括:
- 将资金从一个账户转移到另一个账户的系统(例如,银行应用程序、支付处理系统、交易平台)。
- 供应链和预订系统,商品和服务的所有权从一方转移到另一方。
- 计费系统,将信息存储在详细记录和摘要记录中。
MongoDB 中的事务最佳实践是什么?
一般来说,建议对数据进行建模,以便将一起数据访问的数据存储在一起。以这种方式建立数据模型后,不仅可以获得更好的性能,而且无需进行事务处理。
对于需要事务的应用程序,请遵循以下最佳实践:
- 将长时间运行的事务分成较小的部分,这样它们就不会超过默认的 60 秒超时。 (请注意,此超时时间可以延长。)确保事务中的操作使用索引,以便快速运行。
- 将每个事务限制为 1,000 项文档修改。
- 确保配置适当的读和写关注 (请注意,从版本 5.0 开始, MongoDB 默认使用必要的多数写关注)。
- 添加适当的错误处理并重试由于暂时性错误而失败的事务。
- 请记住影响多个分片的事务的性能成本。有关这些最佳实践的更多信息,请查阅“MongoDB 上的多文档 ACID 事务”白皮书以及 MongoDB 事务文档。
