[数据库]数据库事务隔离级别及封锁协议

1.事务的基本概念

DBMS运行的基本工作单位是事务,事务是用户定义的一个数据库操作序列,这些操作序列要么全做,要么全不做,是一个不可分割的工作单位。事务具有以下特性:
(1)原子性(Atomicity)。 事务是数据库的逻辑工作单位,事务的原子性保证事务包含的一组更新操作是原子不可分的,也就是说,这些操作是一个整体,不能部分地完成。
(2)一致性(Consistency)。 一致性是指使数据库从一个一致性状态编导另一个一致性状态。例如,在转账的操作中,各账户金额必须平衡。一致性与原子性是密切相关的,一致性在逻辑上不是独立的,它由事务的隔离性来表示。
(3)隔离性(Isolation)。 隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。它要求即使有多个事务并发执行,但看上去每个事务按串行调度执行一样。这一性质也称为可串行性,也就是说,系统允许的任何交错操作调度等价于一个串行调度。
(4)持久性(Durability)。 持久性也称为永久性,是指事务一旦提交,改变就是永久性,无论发生何种故障,都不应该对其有任何影响。
事务的原子性、一致性、隔离性和持久性通常称为ACID特性。

2.数据不一致问题

数据库的并发操作会带来一些数据不一致问题,例如,丢失修改、读“脏数据”和不可重复读等。
(1)丢失修改。 事务A与事务B从数据库中读入同一数据并修改,事务B的提交结果破坏了事务A提交的结果,导致事务A的修改被丢失。例如,有T1、T2两个事务,其执行顺序如表所示。则“③A=A-5,写回”操作会被“A=A-8,写回”操作覆盖掉,“③A=A-5,写回”将不起任何作用。

T1 T2
读A=10
读A=10
A=A-5,写回
A=A-8,写回

(2)读“脏数据”。 事务A修改某一数据,并将其写回磁盘,事务B读取同一数据后,事务A由于某种原因被撤销,这时事务A已修改过的数据恢复原值,事务B读到的数据就与数据库中的数据不一致,是不正确的数据,称为“脏数据”。例如,有T1、T2两个事务,其执行顺序如表所示。则T2中“读A=70”就是读的脏数据。

T1 T2
读A=20
A=A+50
写回70
读A=70
ROLLBACKA恢复为20

(3)不可重复读。 不可重复读是指事务A读取数据后,事务B执行了更新操作,事务A适应的仍是更新前的值,造成了数据不一致性。例如,有T1、T2两个事务,其执行顺序如表所示。

T1 T2
读A=20
读B=30
求和=50
读A=20
A=A+50
写A=70
读A=70
读B=30
求和=100(验算不对)

在表中,T1事务为了确保重要计算无误,所以采用了验算的方式,两次独立取出数据并运算,最后进行验算(即比较两次运算结果是否相同)。在此处,虽然两次计算都没错,但由于在两次操作之间的时间间隔中,T2对数据进行了修改,导致验算结果不正确,这就是不可重复读问题。
包括以下情况:
(1) 虚读:事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
(2) 幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。

3.封锁协议

处理并发控制的主要方法是采用封锁技术,主要有两种封锁,分别是X封锁和S封锁。
(1)排它型封锁(X封锁)。 如果事务T对数据对象A(可以是数据项、元组和数据集,以致整个数据库)实现了X封锁,那么只允许事务T读取和修改数据A,其他事务要等事务T解除X封锁后,才能对数据A实现任何类型的封锁。可见,X封锁只允许一个事务独锁某个数据,具有排他性。
(2)共享型封锁(S封锁)。 X封锁只允许一个事务独锁和使用数据,要求太严。需要适当从宽,例如,可以允许并发读,但不允许修改,这就产生了S封锁的概念。S封锁的含义是,如果事务T对数据A实现了S封锁,那么允许事务T读取数据A,但不能修改数据A,在所有S封锁解除之前,决不允许任何事务对数据A实现X封锁。

在多个事务并发执行的系统中,主要采取封锁协议来进行处理,常见的封锁协议如下:
(1)一级封锁协议。 事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。一级封锁协议可防止丢失修改,并保证事务T是可恢复的,但不能保证可重复读和不读“脏数据”。
(2)二级封锁协议。 一级封锁协议加上事务T在读取数据R之前先对其加S锁,读完后即可释放S锁。二级封锁协议可防止丢失修改,还可防止读“脏数据”,但不能保证可重复读。
(3)三级封锁协议。 一级封锁协议加上事务T在读取数据R之前先对其加S锁,直到事务结束才释放。三级封锁协议可防止丢失修改、读“脏数据”,且能保证可重复读。
(4)两段锁协议。 所有事务必须分两个阶段对数据项加锁和解锁。其中扩展阶段是在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁;收缩阶段是在释放一个封锁之后,事务不能再申请和获得其他封锁。若并发执行的所有事务均遵守两段封锁协议,则对这些事务的任何并发调度策略都是可串行化的。遵守两段封锁协议的事务可能发生死锁。
显然,使用封锁技术来解决并发控制问题,存在一个封锁粒度问题。所谓封锁粒度,是指被封锁对象的大小,在关系数据库中封锁粒度有属性值,属性值集,元组,关系,某索引项(或整个索引)、整个关系数据库、物理页(块)等几种。封锁粒度小则并发性高,但开销大;封锁粒度大则并发性低但开销小,综合平衡照顾不同需求,以合理选取适当的封锁粒度是很重要的。

4.死锁问题

采用封锁的方法虽然可以有效防止数据的不一致性,但封锁本身也会产生一些麻烦,最主要就是死锁问题。死锁是指说多个用户申请不同封锁,由于申请者均拥有一部分封锁权,而又需要等待另外用户拥有的部分封锁而引起的永无休止的等待。

5.事务隔离级别

事务隔离级别是封锁协议的应用。

序号 英文名 中文名 描述
1 read_uncommited 读未提交 允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
2 read_commited 读已提交 允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
3 repeatable_read 可重复读 禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
4 serilizable 序列化读 提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

你可能感兴趣的:(数据库篇)