《数据密集型应用系统设计》- 事务

在一个苛刻的数据存储环境中,有很多可能出错的场景,如数据库网络连接错误、部分更新、数据覆盖以及各种 并发场景下读写产生的奇怪问题,事务技术是解决此类问题的首选方案,它可以将多个读写操作绑定在一起成为 一个逻辑单元,作为一个整体要么成功提交要么失败回滚,不会出现中间状态,简化应用层处理逻辑。

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年提出,未被广泛使用,但未来可能成为数据库标准。

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @sxzhou Jan 3, 2023

奉献爱心