Skip to the content.

事务

事务可以处理很多复杂的场景,以及各种位置的错误情况(网络错误、进程崩溃、断电、磁盘已满、并发竞争等)会导致数据不一致的情况。

在这些错误模式下,我们也有相应的数据隔离级别:读-提交、快照隔离(或可重复读)与可串行化。这些隔离级别可以处理下面这些边界条件

可串行化的隔离要求最严格,如果一个事务执行的时间非常短,并且单个 CPU 核可以满足事务的吞吐要求,那么直接使用可串行化的隔离是一个非常合适的方案。

多版本并发控制(MVCC)

快照级别隔离

其总的实现方式是,每个事务都从数据库的一致性快照中读取数据,事务一开始所看到的是最近提交的数据,即使随后数据会被其他事务所更改,但保证每个事务都只看到该特定时间点的旧数据。

实现方式通常是采用写锁来防止脏写,也就是说正在进行写操作的事务会阻塞同一对象上的其他事务操作。但是读操作是不需要加锁的。所以从性能上看,这与读锁不同,写锁是不会阻塞读操作的。

还有一种更加通用的解决方案,那就是多版本并发控制:考虑到多个正在进行的事务可能会在不同的时间查询数据,所以数据库保留了对象多个不同的提交版本

通过前面的学习的读提交隔离级别要求,也是可以通过新旧两个版本的值来避免脏读,其实也可以用 MVCC 来实现读-提交隔离。在读-提交隔离下,只保留对象的两个版本即可:一个已提交的旧版本和一个未提交的新版本。

两阶段加锁(two-phase locking,2PL)

两阶段加锁类似读-提交隔离实现方法,通过加锁来防止脏写。但是灵活度更高,多个事务可以同时读取同一个对象,但只要涉及到任何写操作,则必须加锁以独占形式操作。

两阶段加锁最大的问题就是性能问题,多个事务只要一旦涉及到写的并发,就会导致彼此互斥,等待独占锁的事务提交完成或中止。所以其事务的吞吐能力和查询相应时间要比其他隔离级别要少。

这里面优化措施就是谓词锁索引区间锁

可串行化快照隔离(SSI)

可串行化快照隔离是一种乐观并发控制。如果可能发生冲突,事务会继续执行而不是中止,寄希望于接下来相安无事;而当事务提交时(只有可串行化的事务被允许提交),数据库才会检查是否确实发生冲突,如果冲突则中止事务并接下来重试。

虽然此种方式性能表现客观,但是要注意,其性能表现是于当提交时刻检查出来的确发生冲突的比例有直接关系的。例如一个运行很长时间的事务,读取和写入大量数据,因而产生的冲突并中止的概率就会增大。所以在使用 SSI 时要求读-写事务尽量要简短。