事务及其隔离级别
事务
事务的特性
事务是作为单个逻辑工作单元执行的一些列操作。一个逻辑工作单元必须具有4个属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)及持续性(Durability),简称ACID属性。
- 原子性:事务必须是原子工作单元,事务中包含的诸操作要么都做,要么都不做。
- 一致性:事务执行的结果必须使数据库从一个一致性状态转变到另一个一致性状态。
- 隔离性:一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他并发执行事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持续性:持续性也称永久性,是指一个事务一旦要提交,它对数据库中数据的改变就应该是永久性地,接下来的其他操作或故障不应该对其执行结果有任何影响。
定义事务
在SQL语言中,定义事务的语句有以下3条:
- BEGIN TRANSACTION
- COMMIT
- ROLLBACK
事务通常是以BEGIN TRANSACTION开始,以COMMIT或ROOLBACK结束。
COMMIT表示提交,即提交事务的所有操作,它保证事务的所有修改在数据库中都永久有效,COMMIT语句还释放事务使用的资源(如锁)。
ROOLBACK表示回滚,即在事务运行的过程中出现错误,或用户决定取消事务,系统将事务中对数据库的所有已完成的操作全部撤销,数据返回到它在事务开始时所处的状态。ROLLBACK还释放事务占用的资源。
事务隔离级别
事务指定一个隔离级别,该隔离级别定义一个事务必须与由其他事务进行的资源或数据更改相隔离的程度。SQL采用SET TRANSACTION ISOLATION LEVEL进行设定。具体格式如下:
SET TRANSACTION ISOLATION LEVEL
{
READ UNCOMMITTED
|READ COMMITTED
|REPEATABLE READ
|SERIALIZABLE
}
[;]
隔离级别 | 脏读 | 不可重复读 | 幻像(虚拟读取) |
---|---|---|---|
READ UNCOMMITTED | 是 | 是 | 是 |
READ COMMITTED | 否 | 是 | 是 |
REPEATABLE READ | 否 | 否 | 是 |
SERIALIZABLE | 否 | 否 | 否 |
READ UNCOMITTED指定语句可以读取已由其他事务修改但尚未提交的行。这一级别运行的事务,不会发出共享锁来防止其他事务修改当前事务读取的数据,也不会被排他锁阻塞。该选项的作用与在事务内所有SELECT语句中的所有表上设置NOLOCK相同。这是隔离级别中限制最少的级别。
READ COMMITTED指定语句不能读取已由其他事务修改但尚未提交的数据,这样可避免脏读,其他事务可在当前事务的各个语句之间更改数据,从而产生不可重复读取和虚拟数据。该选项是SQL Server的默认设置。READ COMMITTED的行为取决于READ_COMITTED_SNAPSHOT数据库选项的设置:如果将READ_COMMITED_SNAPSHOT设置为OFF(默认设置),则数据库引擎会使用共享锁防止其他事务在当前事务执行读取操作期间修改行。共享锁类型确定它将于何时释放。行锁在处理下一行之前释放。页锁在读取下一页时释放,表锁在语句完成时释放。
REPEATABLE READ指定语句不能读取已由其他事务修改但尚未提交的行,并且指定其他任何事务都不能在当前事务完成之前修改由当前事务读取的数据。对事务中的每个语句所读取的全部数据都设置了共享锁,并且该共享锁一直保持到事务完成为止,可以防止其他事务修改当前事务读取的任何行。其他事务可以插入与当前事务所发出语句的搜索条件相匹配的新行。如果当前事务随后重试执行该语句,它会检索新行,从而产生虚拟读取。
SERIALIZABLE语句不能读取已由其他事务修改但尚未提交的数据,任何其他事务都不能在当前事务完成之前修改由当前事务读取的数据,不能使用当前事务中任何语句读取的键值插入新行。范围锁处于与事务中执行的每个语句的搜索条件相匹配的键值范围之内,在事务完成之前将一直保持范围锁。这是限制最多的隔离级别,因为并发级别较低,所以应只在必要时才使用该选项。该选项的作用与在事务内所有SELECT语句中的所有表上设置HOLDLOCK相同。
实验操作
设计并执行一个复杂事务
编写一个事务:对于《数据结构》书只有5本,读者“李伟”打算借阅这本书,该读者能否借阅到这本书,给出结果提示。
- 新建三张数据表:book、borrow、reader,并给定部分必要的数据:
- 新建查询,检查是否有语法错误,执行SQL语句:
begin transaction
use TRANS_TEST
go
declare @bookID int,@readerID int,@borrow_date date,@borrowno1 int,@borrowno2 int,@booknum int
set @borrow_date=GETDATE()
select @bookID