现代应用不在真空中运行。每次预订乘车、购买商品或更新余额时,后端都要处理多个操作(读取、写入、验证),通常跨不同表或服务。这些操作必须要么一起成功,要么作为整体失败。
这就是事务介入的地方。
数据库事务将一系列操作包装成全部或全无的单元。要么整个提交并对世界可见,要么都不。换句话说,目标是没有半途而废的订单、没有不一致的账户余额、没有幽灵预订。
然而,当并发进入画面时,维护正确性变得更难。
这是因为事务不在隔离中运行。真实系统处理数十、数百或数千同时用户。每个用户都期望他们的操作成功。在幕后,数据库必须平衡隔离、性能和一致性,而不使系统停滞。
这个平衡行为不平凡。以下是一些情况:
- 一个事务可能读取另一个即将更新的数据
- 两个用户可能尝试预订相同库存槽
- 后台作业可能在客户点击”确认”前锁定记录
这些场景可能导致冲突、竞态条件和死锁,完全停滞系统。
在本文中,我们分解使事务系统在面对并发时可靠的关键构建块。我们将从基础开始:什么是事务,以及为什么 ACID 属性重要。然后我们将深入并发控制机制(悲观和乐观)并理解相关权衡。
ACID 属性
原子性(Atomicity)
事务中的所有操作要么全部成功,要么全部失败。没有中间状态。
一致性(Consistency)
事务执行后,数据库必须保持在一致状态。所有约束、触发器和规则必须满足。
隔离性(Isolation)
并发执行的事务必须相互隔离。一个事务的中间状态对其他事务不可见。
持久性(Durability)
一旦事务提交,更改永久保存,即使系统故障。
并发控制
悲观并发控制
假设冲突会发生,提前获取锁防止冲突。
优点:
- 防止冲突
- 适合高冲突场景
缺点:
- 可能导致死锁
- 降低并发性能
乐观并发控制
假设冲突很少发生,在提交时检查冲突。
优点:
- 高并发性能
- 无死锁风险
缺点:
- 冲突时需要重试
- 不适合高冲突场景
本文为学习目的的个人翻译,译文仅供参考。
原文链接:A Guide to Database Transactions: From ACID to Concurrency Control。
版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。