在一个苛刻的数据存储环境中,有很多可能出错的场景,如数据库网络连接错误、部分更新、数据覆盖以及各种
并发场景下读写产生的奇怪问题,事务技术是解决此类问题的首选方案,它可以将多个读写操作绑定在一起成为
一个逻辑单元,作为一个整体要么成功提交要么失败回滚,不会出现中间状态,简化应用层处理逻辑。
ACID
1983年提出用于描述数据库容错机制。需要注意各家数据库厂商宣称的”兼容ACID”其实实现方式并不相同,达到
的效果也是各不相同,甚至相同术语在不同厂商解释都有区别,因此这个词语更像是营销手段。
原子性
作者认为使用可终止性
更准确,即出错时终止操作并丢弃修改。
一致性
概念很宽泛,不同场景解释不一样。这里解释为:对数据有特定预期状态,任何更改必须满足约束。
作者认为ACID这个缩写就是为了构造单词强行拼接,一致性不是数据库提供的基础能力,而来自应用层,不应该和AID并列。
可以这样理解,应用层通过AID,可以实现C。
P.S. 周志明在凤凰架构
中同样提到,这个概念很有误导性,很多书籍博客强行解释其关联,与之对应的BASE
更是离谱,
完全无法说明其基本含义。
隔离性
并行执行的多个事务互不影响,和串行依次执行效果一样。经典数据库教材将隔离定义为串行化,但由于性能问题使用很少,
数据库厂商甚至很多根本没有实现,一般退而求其次使用更低一些的隔离级别。
持久性
事务一旦提交,数据变更不会丢失,写入非易失性存储。
弱隔离级别
读-提交
- 防止脏读
只能看到已提交数据,实现方式:事务执行过程中,同时存在新老值,只有提交才会替换。
- 防止脏写
只会覆盖已提交数据,实现方式:目标对象加锁。
快照隔离级别与可重复读
不可重复读/读倾斜
事务过程中由于存在并行事务修改,因此两次读取同一个值结果不同。
MVCC
解决读倾斜,每个事务对应一个快照版本,事务运行期间基于同一版本。
防止更新丢失
场景: 读-改-写
- 原子操作
update counter set value = value + 1 where key = 1234
- 显式加锁
select for update
- 自动检测更新丢失
并非所有厂商都支持,比如常用的MySql/InnoDB
- CAS
update t set content = new_content where content = old_content
写倾斜
先查询数据,然后根据查询结果做出决定,然后回写数据,事务提交时,之前支持决定的前提条件已不符合。(医生排班、会议室预定)
只能使用”串行化”隔离解决。
幻读
先查询数据,其他事务提交,改变了查询结果。使用区间锁解决。
可串行化
只有可串行化的隔离才能解决以上问题。
- 严格串行执行
对于执行非常快的事务,单线程串行满足吞吐量,则是最佳方案。
- 两阶段加锁
悲观锁,先加共享锁,如果要写,再加写锁。
- 可串行化快照隔离(SSI)
新的算法,2008年提出,未被广泛使用,但未来可能成为数据库标准。