数据库事务与 ACID
事务允许你将一系列操作组合成单个逻辑工作单元。这确保了事务内的所有操作要么全部成功完成(提交,committed),要么全部都不执行(回滚,rolled back),从而防止出现可能导致数据不一致的部分更新。
ACID 特性是一组保证数据库事务可靠性的核心原则。
1. 理解 ACID 特性
ACID 是四个英文单词的首字母缩写:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性共同定义了可靠数据库事务的标准特征。
让我们来详细探讨每一个特性:
1.1 原子性 (Atomicity)
原子性确保事务被视为一个单一的、不可分割的工作单元。这意味着事务中的所有操作要么全部成功完成,要么全部都不执行。如果事务的任何一个环节失败,整个事务都会被回滚,数据库将恢复到事务发生前的原始状态,就像这个事务从未发生过一样。
- 示例: 想象一个银行转账事务,包含两个操作:从账户 A 扣款,向账户 B 入账。原子性确保扣款和入账操作要么都成功,要么在其中一个失败时撤销另一个,从而防止资金凭空消失或凭空出现。
- 假设场景: 假设你在网上购买商品。这个事务涉及多个步骤:更新库存、创建订单记录、处理付款。如果“处理付款”在“更新库存”之后但在“创建订单”之前失败了,原子性特性将确保“更新库存”这一步被回滚撤销。这能防止商品在没有对应订单和付款的情况下被错误地标记为已售出。
1.2 一致性 (Consistency)
一致性确保事务将数据库从一个“有效状态”转换到另一个“有效状态”。这意味着事务在执行前后,都必须严格遵守数据库的所有定义规则、约束条件和数据完整性要求。如果事务试图违反这些规则中的任何一条,它就会被回滚,数据库将维持其原本的一致状态。
- 示例: 考虑一个数据库约束条件:确保银行账户的余额不能低于零。如果一个事务试图扣除一笔会导致账户出现负余额的金额,它就违反了此约束,因此会被系统自动回滚,从而维护了数据库的一致性。
- 现实应用: 在电商系统中,一致性可以确保当用户下订单时,库存被正确扣减,客户账户被准确扣款,订单详细信息被完整记录,且整个过程完全符合预定义的业务规则。
- 假设场景: 想象一个学生成绩管理系统。一致性规则可能规定学生的成绩必须在特定范围内(例如 0 到 100 分)。如果一个事务试图录入一个超出此范围的成绩,该事务将被回滚,以确保数据库数据的合法性和一致性。
1.3 隔离性 (Isolation)
隔离性确保并发执行的事务之间不会相互干扰。每个事务在执行时,都应该感觉像是当前数据库中唯一正在运行的事务。这可以防止一个事务在另一个事务尚未提交之前,读取到其产生的中间(未完成)数据。隔离性通常通过数据库的锁机制来实现。
不同的“隔离级别”提供了不同程度的保护,以应对并发带来的问题。选择哪种隔离级别取决于应用程序的具体要求,需要在“数据一致性”与“并发性能”之间取得平衡。我们将在后续关于并发控制的章节中更详细地探讨隔离级别。
- 示例: 假设两个并发事务正试图更新同一个银行账户的余额。隔离性确保其中一个事务的更新,在它最终提交之前,对另一个事务是不可见的。这防止了“更新丢失”的问题,并确保最终的计算余额是正确的。
- 现实应用: 在航班预订系统中,隔离性确保当两个用户同时尝试预订某个航班上的最后一个座位时,最终只有一个人能成功。系统必须防止两个用户预订到同一个座位从而导致航班超售。
- 假设场景: 考虑一个酒店客房预订系统。如果两个事务试图在相同的日期预订同一间客房,隔离性确保只有一个事务能操作成功,有效防止重复预订(Double-booking)。
1.4 持久性 (Durability)
持久性确保一旦事务被成功提交,其对数据库所做的更改就是永久性的。即使随后发生停电、系统崩溃等严重故障,这些更改也会被保存下来。这通常是通过将事务日志实时写入硬盘等持久化存储设备来实现的。
- 示例: 当一笔银行转账事务提交后,持久性保障了账户余额的变更被永久记录。即使数据库服务器在事务提交后的下一秒立刻崩溃,数据也不会丢失。
- 现实应用: 在金融交易系统中,持久性确保一旦一笔交易被执行并提交,交易的详细信息将被永久留存且不可丢失,即使系统发生灾难性故障也是如此。这对于满足监管合规要求和进行准确的财务记录保存极其关键。
- 假设场景: 想象一个电子病历管理系统。持久性确保一旦医生更新了患者的诊断记录并提交了事务,这些重要更改就会被永久安全地存储起来,不会因系统突然崩溃而意外丢失。
2. 实际示例与演示
为了更好地说明 ACID 特性在实际业务中的运用,让我们考虑一个涉及两个账户之间进行银行转账的简化场景。假设我们有一个名为 accounts 的表,包含 account_id(账户ID) 和 balance(余额) 列。
- 原子性表现: 如果从转出账户的扣款操作成功了,但由于目标接收账户异常导致入账操作失败(例如被另一个并发事务耗尽了接收额度),那么整个转账事务必须回滚,之前成功的扣款操作也会被撤销。
- 一致性表现: 在转账事务发生前和发生后,所有相关账户余额的“总和”必须保持绝对不变。如果事务违反了此规则(例如,系统漏洞凭空创造了钱或把钱弄丢了),它必须被立刻回滚。
- 隔离性表现: 如果有两笔转账事务同时在相同的两个账户之间转移资金,那么在第一笔事务完全提交之前,它所做的余额修改对第二笔事务应该是不可见的。这有效防止了竞态条件,确保了余额计算的准确无误。
- 持久性表现: 一旦转账事务提示“成功”(已提交),对两个账户余额的修改数据就必须被永久落盘存储,哪怕机房下一秒断电也能在重启后留存下来。