一、事务的概念
所谓事务是用户定义的一个数据操作序列,这些操作可作为一个完整地工作 单元,要么全部执行,要么全部不执行,是一个不可分割的工作单位。
事务与程序的不同:程序是静止的,事务是动态的,是程序的执行而不是程 序本身;同一程序的多个独立执行可以同时进行,每一步执行则是一个不同 的事务。
在 SQL 中,用户显示定义事务的语句一般有三条:BEGIN TRANSACTION、 COMMIT 和 ROLLBACK,且事务通常是以 BEGIN TRANSACTION 语句开始,以 COMMIT 语句或 ROOLBACK 语句结束。
事务中的操作一般是对数据的更新操作,包括增、删、改。
二 、事务的特征
1、 原子性:
事务的原子性保证事务包含的一组更新操作是原子不可分的,即事 务是不可分割的最小工作单位,所包含的这些操作是一个整体。
2、 一致性:
一致性要求事务必须满足数据库的完整性约束,且事务执行完毕后将数据库由一个一致性状态转变到另一个一致性状态。其中,数据库的一致性状态是一种以一致性规则为基础的逻辑属性。
3、 隔离性:
隔离性要求事务是彼此独立的、隔离的,即一个事务的执行不能被其他事务所干扰,一个事务对数据库变更的结果必须在它COMMIT后,另一 个事务才能存取。
4、 持续性:
持续性也称为永久性,是指一个事务一旦提交,它对数据库中数据 的改变就应该是永久性的,且接下来的其他操作或故障不应该对其执行结果 又任何影响。
三、并发操作问题
事务是并发控制的基础单位,保证事务的 ACID 特征是事务处理的重要任务, 而事务的 ACID 特征可能遭到破坏的原因之一是多个事务对数据库的并发操作造成的。为了保证事务的隔离性和一致性,DBMS 需要对并发操作进行正确调 度。其中,完整性检验可以保证一个事务单独执行时,若输入的数据库 状态是 正确的,则其输出的数据库状态也是正确的。但当多个事务交错执行时,可能 出现不一致问题,这也称为并发操作问题,典型的有如下三种:丢失更新、不 可重复读和读“脏”数据。
1、 丢失更新
设有两个事务 T1 和 T2,当它们同时读入同一数据并加以修改时,事务 T2 的提 交结果会破坏事务 T1 提交的结果,由此导致事务 T1 的修改被丢失。这就是一 种由于对数据的并发操作而带来的数据不一致。
2、 不可重复读
不可重复读包括三种情况:
(1) 事务 T1 读取某一数据后,事务 T2 对其做了修改,当事务 T1 再次读该数 据时,得到与前一次不同的值。
(2) 事务 T1 按一定条件从数据库中读取了某些数据记录后,事务 T2 删除了其 中部分记录,当事务 T1 再次按相同条件读取数据时,发现某些记录神秘地消失了。
(3) 事务 T1 按一定条件从数据库中读取某些数据记录后,事务 T2 插入了一些 记录,当事务 T1 再次按相同条件读取数据时,发现多了一些记录。
3、 读“脏”数据
设有两个事务 T1 和 T2,读“脏”数据是指,事务 T1 修改某一数据,并将其写回 磁盘,事务 T2 读取同一数据后,事务 T1 由于某种原因被撤销,这时事务 T1 已修改过的数据恢复原值,事务 T2 读到的数据就与数据库中的数据不一致, 则事务 T2 读到的数据就为“脏”数据,即不正确的数据。
解决并发操作所带来的数据不一致性问题的方法有封锁、时间戳、乐观控制法 和多版本并发控制等。
四、封锁
封锁是最常用的并发控制技术,它的基本思想是:需要时,事务通过向系统请求对它所希望的数据对象加锁,以确保它不被非预期改变。
1、 锁
一个锁实际上就是允许或组织一个事务对一个数据对象的存取特权。 基本的封锁类型有两种:排他锁和共享锁。
2、 用封锁进行并发控制
工作原理是:
(1) 若事务 T 对数据 D 加了 X 锁,则所有别的事务对数据 D 的锁请求都必须 等待直到事务 T 释放锁。
(2) 若事务 T 对数据 D 加了 S 锁,则别的事务还可对数据 D 请求 S 锁,而对 数据 D 的 X 锁请求必须等待直到事务 T 释放锁。
(3) 事务执行数据库操作时都要先请求相应的锁,即对读请求 S 锁,对更新 (插入、删除、修改)请求 X 锁。这个过程一般是由 DBMS 在执行操作时自动隐含地进行。
(4) 事务一直占有获得的锁直到结束(COMMIT 或 ROLLBACK)时释放。 因此,利用封锁机制可以解决并发操作所带来的三个不一致问题。
3、 封锁的粒度
以粒度来描述封锁的数据单元的大小。DBMS 可以决定不同粒度的锁。由最底 层的数据元素到最高层的整个数据库,粒度越细,并发性就越大,但软件复杂性和系统开销也就越大。
4、 封锁的级别
封锁的级别又称为一致性级别或隔离度。 由各种锁的类型与其封锁期限组合可形成不同的封锁级别:
(1)0 级封锁 (2)1 级封锁 (3)2 级封锁 (4)3 级封锁
5、活锁与死锁
在并发事务处理过程中,由于锁会使一事务处于等待状态而调度其他事务处 理,因而该事务可能会因优先级低而永远等待下去,这种现象称为“活锁”。 活锁问题的解决与调度算法有关,一种最简单的办法是“先来先服务”。
两个以上事务循环等待被同组中另一事务锁住的数据单元的情形,称为“死 锁”。DBMS 需要提供死锁预防、死锁检测和死锁发生后的处理技术与方法。
预防死锁的办法在操作系统中已普遍讨论,其中主要有如下几种:
(1) 一次性锁请求 (2) 锁请求排序 (3) 序列化处理 (4) 资源剥夺
6、可串行性
一组事务的一个调度就是它们的基本操作的一种排序。若在一个调度中,对于 任意两个事务 T1 和 T2,要么 T1 的所有操作都在 T2 所有操作之前,要么反之,则该调度是串行的,因而是正确的。 通常,在数据库系统中,可串行性就是并发执行的正确性准则,即当且仅当一 组事务的并发执行调度是可串行化,才认为它们是正确的。
7、两段封锁法
采用两段封锁法是一种最简单而有效的保障封锁其调度是可串行性的方法。 两段锁协议规定在任何一个事务中,所有加锁操作都必须在所有释放锁操作之前。其中,事务划分成如下两个阶段。
(1) 发展(Growing)或加锁阶段
(2) 收缩(Shrinking)或释放锁阶段
定理:遵循两段锁协议的事务的任何并发调度都是可串行化的。