SQLserver锁和事务隔离级别的比较与使用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
SQLserver锁和事务隔离级别的比较与使用
对象
①锁:每条SQL语句
②隔离:事务
锁
①
并发问题
丢失更新
未确认的读取(脏读)
不一致的分析(非重复读):多次读取相同的数据(行)不一致(其他用户更改update)
幻像读:多次读取有不存在和新增的数据(其他用户插入insert或删除delete)
隔离级别
幻像说明
隔离级别脏读不可重
复读取
未提交读(read uncommitted)是是是如果其他事务更新,不管是否提交,立即执行
提交读(read committed默认)否是是读取提交过的数据。如果其他事务更新没提交,则等待可重复读(repeatable read)否否是查询期间,不允许其他事务update
可串行读(serializable)否否否查询期间,不允许其他事务insert或delete
对数据已经具有排他锁(只能有一个),其他的事务就不能对锁定的数据获取共享锁和排他锁(即排他锁与共享锁不能兼容,更多信息请查看锁兼容性),在此特别强调一下锁定的数据,因为有的资料上讲解到“一个连接写的时候,另一个连接可以写”,实际上写的这种情况是各个连接的读写的数据不是相同的行,也就是说各个连接锁定的数据不同。
根据以上分析,我们总结为六个字为“共享读,排他写”。
了解了锁的情况之后,又涉及到一个问题。事务究竟要保持锁多久呢?
一般来说,共享锁的锁定时间与事务的隔离级别有关,如果隔离级别为Read Committed 的默认级别,只在读取(select)的期间保持锁定,即在查询出数据以后就释放了锁;如果隔离级别为更高的Repeatable read或Serializable,直到事务结束才释放锁。另说明,如果select语句中指定了HoldLock提示,则也要等到事务结束才释放锁。
排他锁直到事务结束才释放。
做出了以上分析,现在我们可能会存在这样的疑问,到底在执行SQL语句的时候发出什么样的锁呢,这就由事务的隔离级别决定了。一般情况,读语句(select)发出共享锁,写语句(update,insert,delete)发出排他锁。但是,如果这样不能满足我们的要求怎么办呢,有没有更多选择呢,别急,SQLserver为我们提供了锁定提示的概念。
锁定提示对SQL语句进行特别指定,这个指定将覆盖事务的隔离级别。下面对各个锁定提示分别予以介绍(更多资料请查看SQLserver的联机帮助),笔者做出了以下分类。
类型1
①READUNCOMMITTED:不发出锁
②READCOMMITTED:发出共享锁,保持到读取结束
③REPEATABLEREAD:发出共享锁,保持到事务结束
④SERIALIZABLE:发出共享锁,保持到事务结束
类型2
①NOLOCK:不发出锁。等同于READUNCOMMITTED
②HOLDLOCK:发出共享锁,保持到事务结束。等同于SERIALIZABLE
③XLOCK:发出排他锁,保持到事务结束。
④UPDLOCK:发出更新锁,保持到事务事务结束。(更新锁:不阻塞别的事物,允许别的
事物读数据(即更新锁可与共享锁兼容),但他确保自上次读取数据后数据没有被更新)⑤READPAST:发出共享锁,但跳过锁定行,它不会被阻塞。适用条件:提交读的隔离级别,
行级锁,select语句中。
类型3
①ROWLOCK:行级锁
②PAGLOCK:页级锁
③TABLOCK:表锁
④TABLOCKX:表排他锁
讲解完锁后,下面结合一个具体实例,具体看一下锁的使用。
在很多系统中,经常会遇到这种情况,要保持一个编号的唯一,如会计软件中的凭证的编号。一种编号的处理是这样的,把表中的最大编号保存到表中,然后在这个编号上累加,形成新的编号。这个过程对并发处理要求非常高,下面我们就来模拟这个过程,看如何保持编号的唯一性。
新建一张表code来保存凭证的最大编号。字段如下:编
号:bh(numeric(18,0)),凭证表名pinzheng(varchar(50))
假设表中有这样的一条记录:
Bh Pinzheng
18000 会计凭证
新建一个存储过程来生成新的凭证编号,如下:
CREATE PROCEDURE up_getbh AS
Begin Tran
Declare @numnewbh numeric(18,0)
select @numnewbh = bh FROM code WITH (UPDLOCK,ROWLOCK) where pinzheng = '会计凭证'
set @numnewbh = @numnewbh + 1
update code set bh = @numnewbh where pinzheng = '会计凭证'
print @numnewbh
Commit tran
GO
然后,打开查询分析器,并多开几个连接(笔者开了8个连接,模拟有8个人同时并发,读者可以开更多的连接进行试验),把类似以下这样的语句复制到每个连接窗口中,declare @i numeric(18,0)
set @i = 1
while @i = 1
Begin
if getdate() > '2004-07-22 14:23' --设定一个时间,到此时间同时执行upgetbh存储过程
set @i = 0
end
exec up_getbh
然后,接连运行各个连接,到2004-7-22 14:23 这一刻,各个连接同时运行up_getbh。从运行结果可以看出连接顺序出现18001开始个数字,并没有重号或丢号的现象。
分析:由于up_getbh中的select语句使用了更新锁,因更新锁之间不能兼容,所以各个连接要等到所有其他的连接释放掉锁才能执行,而更新锁的释放要等到事务结束,这样就不会发生号出错的现象了。
附:锁的兼容性表
现有的授权模式