begin tran
update EB with(rowlock) set Name='abc123' where Code=12 waitfor delay '00:00:13'
commit tran我用rowlock想锁定一行,可是它把整个表都锁定了。不管我更新哪一条数据都要等这条更新完毕。怎么才能锁定一条记录啊?
update EB with(rowlock) set Name='abc123' where Code=12 waitfor delay '00:00:13'
commit tran我用rowlock想锁定一行,可是它把整个表都锁定了。不管我更新哪一条数据都要等这条更新完毕。怎么才能锁定一条记录啊?
◆问题描述
想在检索一批数据的同时,加上更新锁,以禁止其他端末的更新。不是表级,页级加锁,自然而然想到了行级加锁。
但是,发现检索出来的记录以外的数据也被加上锁了。 ◆问题分析
tabale
----------------------------------------------------字段 C1(key1) C2( key2) C3 ............
c1001 c2003 c3001
c1002 c2002 c3001
c1003 c2001 c3001索引 IX_TEST (C2,C1)
----------------------------------------------------用户A,用户B,公用部分SQL
set lock_timeout 0
begin transaction
select * from TEST with (index=IX_TEST,rowlock,updlock)CASE①
用户A
where C1 ='c1003'
结果-- c1003 c2001 c3001 (Locked)用户B
where C1 ='c1003'
结果-- 已超过了锁请求超时时段。 和预想的一样where C1 ='c1002'
结果-- 已超过了锁请求超时时段。 感觉这行也被锁住了where C1 ='c1001'
结果-- 已超过了锁请求超时时段。 感觉这行也被锁住了
分析--CASE①的结果来看,用户A对第三条数据加锁的同时,对第一第二条数据也加上锁了
(这个不是我们预想的结果)
那我们再接着看。。
CASE②
用户A
where C1 ='c1003'
结果-- c1003 c2001 c3001 (Locked)用户B
where C2 ='c2001'
结果-- 已超过了锁请求超时时段。 和预想的一样where C2 ='c2002'
结果-- c1002 c2002 c3001 (发现没有被用户A锁住)where C2 ='c2003'
结果-- c1001 c2003 c3001 (发现没有被用户A锁住)分析--CASE②的结果来看,用户A只对第三条数据加了锁,其他两条没有被锁住
(这符合我们预想的结果)
结合起来分析看,CASE①和CASE①的用户B只是用了不同的检索条件检索相同的记录,
但得出的加锁状态结果却不同。这是为什么呢?继续分析
1.毫无疑问,首先,SQLSERVER的行级锁和表的索引有密切的关系,他是按照索引的顺序
去检索数据,并且在相应的记录上加锁。(默认索引是表的主键顺序-升序?)
2.查了一些论坛,有的是这么解释的,说是如果where条件中没有索引字段的相应条件的话
所有记录都会被上锁,相当于变成了表级锁。从上面的例子看不这么回事,锁还是该加在哪行就是在哪行的。
只不过是在第一次给某些记录加上了锁的基础上,再一次给这个表的某些记录去加锁的时候,SQLSERVER是
根据索引怎么去加锁的呢。上面的例子可以看出,不同的where条件,SQLSERVER去遍历记录的顺序是不同。
从两个case猜测,加锁的方式是,先根据WHERE条件确定在索引中位置. 遍历记录并给记录加锁,符合WHERE
中所用条件的记录情况下,就不解锁,如果不符合的话就解锁,这就把要加锁的记录加上了锁。回头再看CASE①索引 IX_TEST (C2,C1)是先按字段C1,再是字段C2排序的,顺序如下
字段 C1(key1) C2( key2) C3 ............
c1001 c2003 c3001
c1002 c2002 c3001
c1003 c2001 c3001用户B因为,用C1作为WHERE条件的C1 就无法准确的定位在索引位置,只能是从头到尾了,
这个时候它就会去遍历被用户A锁上的那条记录,因为已经锁上了,再加一次锁就会出现
【已超过了锁请求超时时段。】这个结果,貌似其他两条也被加上锁了。CASE②的用户B因为能定位在索引中位置,所以没有再去遍历被用户A加上锁了的那条数据
所以能正确的检索出结果来。====================================================================== 总结,在操作行级别锁的时候,WHERE条件中必须要明确的确定在索引中位置的WHERE条件。
也就是说按照索引的顺序where C1 =,[C2=]。
use pubs
BEGIN TRAN
UPDATE AUTHORS SET CITY=CITY+'D' WHERE AU_ID='274-80-9391'
--先运行连接1,然后2
--连接1
BEGIN TRAN
UPDATE AUTHORS SET CITY=CITY+'D' WHERE AU_ID='274-80-9391'--ROLLBACK TRAN--连接2:可以update 或 select,说明没有锁住其它行
BEGIN TRAN
UPDATE AUTHORS SET CITY=CITY+'D' WHERE AU_ID='341-22-1782'
--SELECT * FROM AUTHORS--ROLLBACK TRAN[/code]
如果Name参与了索引, Name的改动会涉及到索引的重新计算, 就会琐住整个表.
没有起到锁定一行效果,还是锁定了整个表。
如果表没有簇索引也没有唯一索引的话是无法使用行级锁
begin tran
update EC with(rowlock) set Name='abc123' where Code=21 waitfor delay '00:00:10'
commit tran在Code 上加索引就能锁住这一行了。