SqlServer事务隔离级别和锁:原子性和同步

之所以有这篇文章,也是因为自己一直以来想要弄明白一个问题,就是事务是否能够提供平时编程语言中同步工具(如临界区,信号量等也就是锁)所提供的原子性(事务具有原子性、一致性、隔离性、持久性),也就是封装在一个事务中的select + 修改 + update是否与编程语言中使用同步工具封装的read + modify + write具有同样的效果(即能保证所有修改都不会丢失),一个是在数据库中操作持久数据,一个是在内存中操作非持久数据。

 

这段时间比较闲,正好有时间好好研究这个问题。google一番后发现网上很多文章及资料都是讲事务隔离级别和锁的,没有对这个问题的直接阐述和解答,本人对这些文章及资料进行阅读和理解并做了一定的验证,得出了本文的结论。请大家明确,本文并不对隔离级别和锁进行深入,严谨,完整的探讨,所有内容仅是个人见解,如果有误,多谢指教。

 

何为事务?对于没有使用begin trans的情况,每一条语句都是一个单独完整的事务,如果使用了begin trans,直到commit或者rollback后才算是一个完整事务

 

事务隔离就是指不同的事务之间需要相对独立的执行环境,不应该相互影响,隔离级别定义了事务间占有及访问数据的相互独立的程度。对于事务间的隔离,SqlServer是通过锁来实现的,不同的隔离级别锁的使用时机和种类有所不同,从而达到不同的隔离级别。

 

何时加锁?加锁并不是语句级别的,也就是说并不像普通编程语言中加锁一样,我们可以随意控制在执行某条语句前加锁,执行完后又解锁,在Sql语句中,加解锁并没有那么大的自由度,加解锁的控制更多的是由数据库引擎本身去做,在引擎执行Sql语句的过程中,会将语句的执行分解为很多个步骤,锁的增加和解除更多的是在这些更细的操作步骤中进行,Sql语句提供给我们的只是更粗级别的控制(通过lock hints)。比如对于语句update tablea set columnb = columnb + 1 where columna = 2而言,数据库会先对表tablea进行搜索,然后获取columnb的数据,对其进行操作加1后,再将其写入数据库,在这里面的每一个步骤前后数据库引擎都可能进行加锁解锁操作。

 

SqlServer的事务隔离级别通过一个事务的数据读取受另外一个事务影响的程度来定义:比如read uncommitted(读未提交),也就是当一个事务A读数据时有可能会读取到另外一个事务B未提交的更改,之所以会有这样的结果,是因为A读取数据时并未加任何锁(这里如果是其他隔离级别会加锁),不管事务B在事务期间怎么加锁都不会阻塞事务A的读取操作。

 

下表为本人对于SqlServer事务隔离级别和锁的理解

 

事务隔离级别

Select

Insert

Delete

Update

read uncommitted

本事务

读取前不加任何锁;

相当于with nolock

因为不加任何锁,所以读取数据前不受任何阻塞,所以其它事务未提交(解锁)的更新可以随意读取到;

insert前加范围锁,insert后加释放范围锁,针对insert数据加排它锁,事务结束后释放排它锁

delete前加范围锁和排它锁,delete后释放范围锁,事务结束后释放排它锁

update前加排它锁,事务结束后释放排它锁

其它事务

本事务结束前不受任何阻塞

read committed  

本事务

加共享锁然后读取;

不等事务结束就释放锁;

默认的lock hint(共享锁)

因为加共享锁,所以在其它更新事务未提交前是无法获取到锁,会被阻塞,所以确保读到的都是提交后的数据;

其它事务

本事务结束前仅受读取瞬间的阻塞;

repeatable read    

本事务

加共享锁然后读取;

等事务结束后释放锁;

相当于with UPDLOCK

(类似但不完全一样)

其它事务

本事务结束前无法update/delete

serializable

本事务

读取完毕后加范围锁;

等事务结束后释放范围锁;

相当于with HOLDLOCK

其它事务

本事务结束前无法update/delete/insert

XLOCK(lock hint)    

本事务

加排它锁然后读取;

等事务结束后释放锁;

相当于with XLOCK

因为加排它锁,所以任何相关的事务(select/update/delete)未提交前本事务会一直被阻塞;

其它事务

本事务结束前无法select/update/delete

 

根据以上的内容,我之前的问题也就有了答案,即数据库中事务的原子性并不包含并发的概念,仅有如下含义:事务内所有语句要么都不执行,要么全部执行,不会出现只执行其中一部分的情况。本质上就是指其存储的持久性数据仅有两种状态,一种是事务开始前的状态,一种是事务执行成功后的状态。而编程语言中的原子性更多的是指不可并发性,也就是说原子操作在整个执行过程中仅能由一个线程或者一个CPU内核来参与,无法并发。从更高的层面看:数据库中的事务的原子性是空间上的概念,而编程语言中的原子性是时间上的概念,两者完全不是一回事。要想在数据库引擎上实现并发,无法直接通过事务来实现,必须依靠数据库自己的锁。如开篇所说的操作:select + 修改 + update,要想实现并发原子性,必须通过在select时加XLOCK来实现。

posted @ 2014-01-06 17:04  huwji_stanley  阅读(847)  评论(0编辑  收藏  举报