数据库基本三个锁的学习体会

工作中碰到死锁的情况,很是无奈,今天偶尔想起这个问题,网上搜了一下,发现数据库锁的学问还真不少,下面就我所了解的坐一下总结:

数据库基本锁有三个,共享锁,排它锁以及更新锁,锁之间有兼容性,具体请看http://technet.microsoft.com/zh-cn/library/ms186396(v=sql.105).aspx

锁兼容性控制多个事务能否同时获取同一资源上的锁。如果资源已被另一事务锁定,则仅当请求锁的模式与现有锁的模式相兼容时,才会授予新的锁请求。

首先,兼容性是针对不同事务的,一个事务就没有兼容性的概念了,类似于多线程与单线程,试想单线程还会有死锁吗,直接就转化了!

其中共享锁跟排它锁不兼容,而排它锁跟谁都不兼容,更新锁跟共享锁兼容,其他都不兼容。我想不兼容的锁相互等待就会导致死锁,也是死锁的唯一原因吧。

共享锁即在select 时自动产生,排它锁在insert,update以及delete时自动产生。

而更新锁不会自动产生,他的作用是防止多个事务执行同样的sql查询及修改导致的死锁,比如两个事务同时执行一个sql查询,而共享锁之间是兼容的,所以没问题,而其中一个事务执行完查询要更新语句,此时共享锁自动转化为排它锁,而此时另一个事务的查询还未结束,所以共享锁还在,而排它锁与共享锁不兼容,所以就无法转化为排它锁(此时仍然保持着共享锁,并没有释放《除非事务结束》),只能等到另一个事务结束后释放这个共享锁,可这个事务查询执行完后,也要执行更新语句及相关记录资源也要转换为排它锁,(实验证明任何锁在事务提交或回滚之前是不会释放的,不过可以相互转化!因为事务具有原子性!)而这个排它锁与另一个事务的共享锁同样不兼容,所以两个事务相互等待释放共享锁,而转换为排它锁来更新资源,就导致了死锁。更新锁可以避免这种死锁的产生,因为更新锁之间是不兼容的,所以一个事务获取了更新锁后,另一个事务就不能再获取更新锁了,不像共享锁可以同时获取,从而导致相互等待释放的后果,更新锁从开始就避免了同时锁定资源的可能性,所以完全避免了相互等待,也就完全避免了死锁!

 上面一段解释了死锁的一种原因及共享锁之间相互等待释放导致的死锁,这是实际程序运行中最常见的死锁,因为排它锁是不会无限时间等待的,因为排它锁事独占锁,他和谁不兼容,所以谁都不用等,总有执行完释放的那一刻!不过通过人工来操作,我们是可以体会到因为排它锁而导致死锁的现象的。下面是一个简单例子:

打开两个查询窗口:其中一个执行下面语句:

create table a (id int ,name nvarchar(20))
go
begin tran
insert a values ('1','a')--开启一个事务,而不提交也不回滚,此时insert 语句产生的排它锁是不会释放的

在另一个窗口中执行:

select COUNT(*) from a with(nolock)--无锁查询,会查出结果为1

select COUNT(*) from a with(readpast)--忽略所有有锁的记录,此时为0

然后执行select * from a --此时是查不出结果的,会无限地等待下去,因为排它锁未释放,默认查询的共享锁与之不兼容,所以就一直等待排它锁的释放,才会返回结果,即使表中已有许多数据,而排它锁只锁了一条记录,但是,查询语句也要等待这一条记录的锁的释放,才会返回结果。 这便是人工手动设置的因为排它锁未释放而导致的死锁(不是相互等待,而是一方无尽的等待!)。

 

posted on 2013-04-30 17:36  zrf  阅读(2581)  评论(0编辑  收藏  举报