跳转至

事务

事务概念

事务的四大特性(ACID):

  • 原子性:所有操作要么全部执行,要么全部不执行。
  • 一致性:事务执行之前和执行之后数据库的完整性没有被破坏。
  • 隔离性:多个事务之间不会相互影响。
  • 持久性:事务一旦提交,对数据库的修改就是永久性的,即使系统崩溃。

事务从活动状态开始,当完成最后一条语句后,事务就进入了部分提交状态。此时,事务已经完成,但对数据库的修改还没有提交,仍有可能失败。在成功写入修改后,就进入提交状态。

如果事务执行失败,为了保证原子性,则需要执行事务的回滚,恢复事务执行前的状态,即事务中止。通常的做法是维护一个日志,记录事务对数据库的修改。

事务的隔离级别:

  • 可串行化:事务串行执行;
  • 可重复读:只允许读取已提交的数据,而且在一个事务两次读取一个数据项期间,其他事务不得更新该数据;
  • 读已提交:只允许读取已提交的数据,但不要求可重复读。比如,在一个事务两次读取一个数据项期间,另一个事务更新了该数据并提交;
  • 读未提交:允许读取未提交的数据。

以上级别都不允许脏写,即如果一个数据项已被另一个尚未提交或中止的事务写入,则不允许对该数据项执行写操作。

并发控制

  • 共享锁 S(Shared Lock):如果一个事务获得了一个数据项的共享锁,则该事务可以读但不可以写,其他事务可以读取该数据项,也不能修改;
  • 排他锁 X(Exclusive Lock):如果一个事务获得了一个数据项的排他锁,则该事务既可读也可写,其他事务不能读取该数据项,也不能修改。

共享锁和共享锁是相容的,而排他锁和排他锁是互斥的。在任何时候,一个数据项上可同时有被不同事务持有的多个共享锁,而排他锁的请求必须等待所有共享锁的释放。

在事务 T 获得了某个数据项的共享锁后,另一个事务 T1 试图获得该数据项的排他锁,T1 必须等待 T 释放共享锁,但如果不停有其他事务申请对该数据项的共享锁,那么 T1 可能一直获取不到排他锁,这称为饿死。避免饿死的方式时,只有满足以下条件,才能通过事务 T 对数据项的 M 型锁申请:

  1. 在该数据项上不存在持有与 M 型锁冲突的锁的其他事务;
  2. 在该数据项上不存在等待加锁且先于事务 T 申请加锁的事务。

多粒度锁

当事务对树上一个结点显式地加上锁时,该事物也给该结点的全部后代隐式地加上了同类型的锁。

问题在于,当申请给某个结点加锁时,需要先判断其子结点是否持有冲突的锁。为了更快地判断锁冲突,在对一个结点显式地加锁前,需要对该结点的祖先结点加上意向锁(Intention Lock)。

IS IX S SIX X
IS 兼容 兼容 兼容 兼容 不兼容
IX 兼容 兼容 不兼容 不兼容 不兼容
S 兼容 不兼容 兼容 不兼容 不兼容
SIX 兼容 不兼容 不兼容 不兼容 不兼容
X 不兼容 不兼容 不兼容 不兼容 不兼容

MySQL 中的加锁级别分为表锁、页锁和行锁。其中,意向锁和行锁不会互斥,只可能和表级锁互斥。

删除和插入

在删除一个数据项前,先在该数据项上请求排他锁。

在插入一个数据项时,在新创建的数据项上加排他锁。

参考

  • 数据库系统概念. 原书第 6 版. 第四部分.