跳转至

事务

1.Transactions(事务)

Transactions是在上层保证原子性(atomic)的的序列操作。假设该事务由操作A和操作B构成,若当事务执行到操作A的时候数据库/主机崩溃了,我们希望操作A不会被执行,因为期望状态是A和B是一起被执行的。

一个经典的例子是从账户A中转帐到账户B中,此时最少需要两个操作:减少账户A的余额,以及增加账户B的余额。此时,若执行到操作A系统就发生了崩溃,那么就会导致整个系统中余额的总量减少,从而产生数据不一致(consistency)。

事务的中间状态与数据库的状态不一致,这是无法避免的,因此常用的设计时事务在中间状态时保留一份自己的副本。然而,一个系统之中常常有多个事务在并发执行,如何对并发事务之间产生的数据交集从而发生的冲突进行处理,是本文讨论的内容。

事务的正确可靠性来源于四个特性,又简称为ACID。

2.ACID-Atomicity,all or nothing

指的是事务的原子性,事务中的操作要么都执行,要么都不执行。

Logging

表示在执行操作前,在磁盘中写入操作日志,这样就可以跟踪未完成的日志和状态,从而实现回滚。

几乎所有数据库系统都使用了该方式。MySQL的实现方式是Undo log,如一条INSERT语句对应了Undo log中的DELETE,这样在发生错误时就可以很方便的进行回滚。

同样的思想也应用于redo log中,称之为WAL(write ahead logging)。做法是在进行更新语句的时候,不先将更新落地到磁盘的数据库文件中,而是先将更新日志顺序写入到磁盘中,仅在内存中对数据进行更新。当更新日志累积到一定程度的时候,才将这些日志落地到磁盘的数据库文件中。一方面减少了在磁盘上的随机I/O,转为顺序I/O,另一方面在数据库发生异常崩溃的时候可以恢复数据。

shadow paging

保存一个粒度为page的副本,当一个page发生变化的时候,通过更换指针到新page上进行更新。

有效率低,磁盘碎片化和数据集无序等问题,因此不常被使用。

3.ACID-Consistency,it looks correct to me

主要讨论的是逻辑的正确性,一致性对于分布式系统来说有更高的重要性。

这又包括数据库一致性和事务一致性。

  • Database consistency:指事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。保证数据库一致性是指当事务完成时,必须使所有数据都具有一致的状态。
  • Transaction consistency:如果已将某个事务的一部分落实到目标数据库,那么该源事务中所有其他作用域内操作也将已落实。

4.ACID-Isolation,as if alone

指的是多个并发运行的事务之间具有隔离性,即多个并发事务之间互不相干。

MySQL中,对于不同的隔离级别有不同的实现方式。可以用视图-每个事务保存数据的一个副本,事务在自己的副本中去进行修改,最后在提交的时候再处理冲突问题;也可以用锁-直接对锁对数据进行隔离,高隔离性的同时效率也相对较低。

5.ACID-Durability,survive failures

指的是事务的修改应该是具有持久性的。

6.并发控制

事务在数据库内部运行是无法确定一个先后顺序的,对于同时提交的事务T1和T2,我们没办法控制是T1先执行还是T2先执行。但基于ACID来说,很容易能够得到一个基本需求:无论哪个事务先执行,我们希望事务之间可以产生一个先后执行的顺序。这称之为Serializability(可串行性)。

换句话来说,我们既然要求事务有原子性,那么在上层逻辑上来说,我们应该看到的结果是T1结束或T2结束后,再执行另一个事务的逻辑结果。而不应该出现“T1执行了部分操作但不是全部操作之后,T2就开始进行操作”的不合逻辑结果。

该思想的也被称之为Serializable Schedule,指事务的调度顺序等同于某一种执行顺序。

有两种常见的序列化。

  • Conflict Serializability:指的是即使不提供隔离手段,并发的事务之间也会存在一个逻辑执行顺序。

观察得到,破坏事务隔离性的是并发事务之间的关键操作。什么是关键操作?r(x)和w(x)以及w(x)和w(x)就是关键操作,因为他们操作的顺序与结果相关。同理,r(x)和r(x)之间就不是关键操作。这也称之为读-写冲突和写-写冲突。

判断多个事务是否为Conflict Serializability的方法可以借助依赖图(Dependency Graph)。依赖图中,每个事务都是一个节点V,冲突操作是一个边,定义如下。

根据S构造一个有向图,节点V是S中的事务,边是事务与事务之间对同一对象的冲突操作。

  • View Serializability:不满足Conflict Serializability的Serializability Schedule。

7.隔离级别

因为事务并发出现的经典问题如幻读、脏读以及不可重复读本质都是一个事务读取到了另外事务的修改。因此,事务的隔离是必要的。

总的来说,事务隔离有两类

以MySQL为例,MySQL支持以下四种隔离级别。

  • 读未提交(read uncommitted):一个事务没提交时,变更就能被其他事务看见
  • 读提交(read committed):一个事务提交之后,变更才能被其他事务看见
  • 可重复读(repeatable read):一个事务的执行过程中,得到的数据总是一致的
  • 串形化(serializable):对于同一行数据,事务会对它所用到的数据加锁

参考与扩展

https://zhuanlan.zhihu.com/p/57579023

https://zh.m.wikipedia.org/zh-hans/ACID