事务处理存在的意义是为了保证系统中所有的数据都是符合期望的,且相互关联的数据之间不会产生矛盾,即数据状态的一致性(Consistency)。而要达成这个目的,就需要下面三个保证:

  • 原子性(Atomic):在同一项业务处理过程中,事务保证了对多个数据的修改,要么同时成功,要么同时被撤销。

  • 隔离性(Isolation):在不同的业务处理过程中,事务保证了各自业务正在读、写的数据互相独立,不会彼此影响。

  • 持久性(Durability):事务应当保证所有成功被提交的数据修改都能够正确地被持久化,不丢失数据。

当然,上面说到的手段仅适合在单个机器中(例如 MySQL)达成,如果是跨机器的外部一致性,就有点捉襟见肘了。

1、本地事务

本地事务是最基础的一种事务解决方案,只适用于单个服务使用单个数据源的场景,直接依赖于数据源本身提供的事务能力来工作的,并不能深入参与到事务的运作过程当中。

1.1 原子性和持久性

实现原子性和持久性的最大困难是 写入磁盘 这个操作并不是原子的,不仅有 写入未写入 状态,还客观地存在着 正在写 的中间状态,于是在数据修改过程中就可能出现下面情况:

  • 未提交事务,写入后崩溃:多个数据操作,前面的已完成修改,后面的操作崩溃,需要将已修改的数据回滚到未修改前的状态保证原子性;

  • 已提交事务,写入前崩溃:数据操作的事务已经完成提交,但是在落盘时崩溃,需要感知到提交事务的修改数据,重新完成落盘保证持久性。

为了完成上述情况下数据的恢复,一般采用 [[Commit Logging]] 的补救措施,来保证数据的原子性和持久性。除此之外,还有一种 [[影子分页]] 的手段来实现事务机制(涉及隔离性与并发锁时,Shadow Paging 实现的事务并发能力就相对有限,因此在高性能的数据库中应用不多)。

1.2 隔离性

如果没有并发,天然就不需要隔离性,但这不可能,为此一般需要使用锁来保证并发条件下数据的串行访问。

现代数据库一般提供下面几种锁机制:

  • 写锁(Write Lock,也叫作排他锁,eXclusive Lock,简写为 X-Lock):如果数据有加写锁,就只有持有写锁的事务才能对数据进行写入操作,数据加持着写锁时,其他事务不能写入数据,也不能施加读锁。

  • 读锁(Read Lock,也叫作共享锁,Shared Lock,简写为 S-Lock):多个事务可以对同一个数据添加多个读锁,数据被加上读锁后就不能再被加上写锁,所以其他事务不能对该数据进行写入,但仍然可以读取。(对于持有读锁的事务,如果该数据只有它自己一个事务加了读锁,允许直接将其升级为写锁,然后写入数据)

  • 范围锁(Range Lock):对于某个范围直接加排他锁,在这个范围内的数据不能被写入。

按照不同锁的组合使用,又将场景隔离等级分为四种模式:

  • 串行化:最高等级,三种锁都加上,牺牲性能来保证事务的隔离。

  • 可重复读:不再添加范围锁,可能造成幻读问题(在事务执行过程中,两个完全相同的范围查询得到了不同的结果集)。

  • 读已提交:对事务涉及的数据加的写锁会一直持续到事务结束,但加的读锁在查询操作完成后就马上会释放,可能会造成不可重复读问题(在事务执行过程中,对同一行数据的两次查询得到了不同的结果)。

  • 读未提交:对事务涉及的数据只加写锁,会一直持续到事务结束,但完全不加读锁,可能造成脏读问题(在事务执行过程中,一个事务读取到了另一个事务未提交的数据)。

幻读、不可重复读、脏读等问题都是由于一个事务在读数据过程中,受另外一个写数据的事务影响而破坏了隔离性。针对这种 一个事务读+另一个事务写 的隔离问题,大多使用多版本并发控制( [[MVCC]] )来进行优化。其基本思路是对数据库的任何修改都不会直接覆盖之前的数据,而是产生一个新版副本与老版本共存,以此达到读取时可以完全不加锁的目的。

悲观场景下面必须加锁,优化策略就是使用乐观并发控制([[OCC]]),但是前提是要事务之间数据存在竞争是偶然情况,没有竞争才是普遍情况。

2、全局事务

全局事务相对于本地事务而言,支持跨多个数据源之间的事务保证能力,XA 架构定义了全局的事务管理器(Transaction Manager,用于协调全局事务)和局部的资源管理器(Resource Manager,用于驱动本地事务)之间的通信接口,能在一个事务管理器和多个资源管理器(Resource Manager)之间形成通信桥梁,通过协调多个数据源的一致动作,实现全局事务的统一提交或者统一回滚。

2.1 两阶段提交

两阶段提交(2 Phase Commit,2PC)协议通过协调者与参与者之间的交互,来决定是否完成多个数据源的事务提交,其流程为两个阶段:准备和提交。

2.2 三阶段提交

为了解决两阶段提交协议的一些缺陷,演化出了三阶段提交(3 Phase Commit,[[3PC]])协议。该协议对单点问题和回滚时的性能问题有所改善,但是它对一致性风险问题并未有任何改进,在这方面它面临的风险甚至反而是略有增加了的。甚至在事务能够正常提交的场景中,三段式因为多了一次询问,性能还要稍微更差一些。

3、共享事务

共享事务(Share Transaction)是指多个服务共用同一个数据源。

4、分布式事务

分布式事务是以服务为视角的,也就是多个服务之间的事务处理。开始前有必要先对 [[CAP]] 分布式理论做出解释,由于该理论的强制要求,导致 ACID 在分布式场景中实现较为困难,为此提出了 [[BASE]] 最终一致性理论来降低要求。