数据库并发控制
事务
事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位
事务的 ACID 特性
事务具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)
- 原子性
事务是数据库的逻辑工作单位,事务中包括的操作要么都做,要么都不做
- 一致性
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态
- 隔离性
一个事务的执行不能被其他事务干扰
- 持续性
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的
事务是恢复和并发控制的基本单位
并发操作带来的数据不一致性
丢失修改(lost update)
两个事务 T1 和 T2 读入同一数据并修改,T2 提交的结果破坏了 T1 提交的结果,导致 T1 的修改被丢失
-- 事务1
update T1 set c1 = c1 + 1 where id = 1;
-- 事务2
update T1 set c1 = c1 + 1 where id = 1;
不可重复读(non-repeatable read)
事务 T1 读取数据后,事务 T2 执行更新操作,使 T1 无法再现前一次读取结果
具体地讲,不可重复读包括三种情况:
-
事务 T1 读取某一数据后,事务 T2 对其进行了修改,当事务 T1 再次读取该数据时,得到与前一次不同的值
-
事务 T1 按一定条件从数据库中读取了某些记录后,事务 T2 删除了其中部分记录,当 T1 再次按相同条件读取数据时,发现某些记录神秘地消失了
-
事务 T1 按一定条件从数据库中读取了某些记录后,事务 T2 插入了一些记录,当 T1 再次按相同条件读取数据时,发现多了一些记录
后两种不可重复读也被称为幻读
读脏数据(dirty read)
事务 T1 修改某一数据并将其写回磁盘,事务 T2 读取同一数据后,T1 由于某种原因被撤销,这时被 T1 修改过的数据恢复原值,T2 读到的数据就与数据库中的数据不一致
封锁与封锁协议
封锁是实现并发控制的一个非常重要的技术。基本的封锁类型有两种:排他锁(简称 X 锁)和共享锁(简称 S 锁)
在运用 X 锁和 S 锁对数据对象加锁时,还需要约定一些规则。例如,何时申请锁、持锁时间、何时释放等。这些规则称为封锁协议。不同级别的封锁协议,达到的系统一致性级别是不同的
一级封锁协议
事务 T 在修改数据 R 之前,必须先对其加 X 锁,直到事务结束才释放
一级封锁协议可以防止丢失修改
二级封锁协议
在一级封锁协议的基础上增加以下规则,事务 T 在读取数据 R 之前必须先对其加 S 锁,读完后即可释放 S 锁
二级封锁协议可以防止脏读
三级封锁协议
在一级封锁协议的基础上增加以下规则,事务 T 在读取数据 R 之前必须先对其加 S 锁,直到事务结束才释放
三级封锁协议可以防止不可重复读
封锁的粒度
封锁对象的大小称为封锁粒度。以关系型数据库为例,封锁对象可以是这样一些逻辑单元:属性值、属性值的集合、元组、关系、索引项、整个索引直至数据库;也可以是这样一些物理单元:页(数据页或索引页)、物理记录等
封锁粒度与系统的并发度和并发控制的开销密切相关。直观地看,封锁的粒度越大,数据库所能够封锁的数据单元就越少,并发度就越小,系统开销也越小;反之,封锁粒度越小,并发度越高,系统开销也就越大
事务隔离级别
隔离级别是对事务隔离性的保证,不同的隔离级别可以消除不同的并发问题
ANSI SQL-92 定义了四个隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | Possible | P | P |
读已提交 | Not Possible | P | P |
可重复读 | N | N | P |
可序列化 | N | N | N |
实际的关系型数据库产品,基于多版本并发控制技术(MVCC)提供了快照隔离,快照隔离无法彻底解决幻读问题
在 Entity Framework Core 中使用事务
引用 Microsoft.EntityFrameworkCore
和 Microsoft.EntityFrameworkCore.Relational
using (var tran = await _context.Database.BeginTransactionAsync(System.Data.IsolationLevel.Serializable))
{
try
{
// do something
await tran.CommitAsync();
}
catch
{
await tran.RollbackAsync();
throw;
}
}
引用
-
王珊,萨师煊. 数据库系统概论. 第五版. 北京:高等教育出版社,2014