2019独角兽企业重金招聘Python工程师标准>>>
在高并发的情况下,关系型数据库死锁是很难避免的问题,研发能做的是,尽量减少死锁的可能性,那么就要求研发对数据库锁的机制有相当的了解,从而针对问题,尽可能的避免死锁的发生。今天抽空,谈一谈关于update语句条件对锁的影响。我看了网络上的一篇文章,大致意思是
在进行update操作时,where条件是否使用主键或索引,对锁的级别将会有影响,具体内容如下:
下面摘自网络的一段问内容:
ROWLOCK告诉 SQL Server只使用行级锁。ROWLOCK语法可以使用在SELECT, UPDATE和DELETE语句中,不过 我习惯仅仅在UPDATE和DELETE语句中使用。如果在UPDATE语句中有指定的主键,那么就总是会引发行级锁的。但是当SQL Server对几个这种UPDATE进行批处理时,某些数据正好在同一个页面(page),这种情况在当前情况下 是很有可能发生的,这就象在一个folder中,创建一个新文件需要较长的时间,而同时你又要去更新该folder中的某些文件。当页面锁引发后,事情就开始变得糟糕了。而如果在UPDATE或者DELETE 时,没有指定主键,数据库当然认为很多数据会收到影响,那样 就会直接引发页面锁,事情同样变得糟糕。
通过指定使用行级锁,这种情况可以得到避免。但是需要小心的是,如果你错误地使用在过多行上,数据库并不会聪明到自动将行级锁升级到页面锁,服务器也会因为行级锁的开销而消耗大量的内存和CPU,直至无法响应。尤其主要留意的是 企业管理器中"管理/当前活动"(Management /Current Activity)这一项。该项会花较长的时间来载入锁的信息。这些信息是十分有用的,当你使用行级锁后,你如果在"锁/处理" (Locks/Processes)下看到几百个锁,一点都不奇怪,而恰恰应该庆幸锁超时和死锁的问题减少了。
验证
为此,我建了一张表,进行update操作。
首先,根据非主键字段MessageId,进行更新,打开一个事务,我们称为sql1吧。
begin tran
update [Messages] set RecordDate=GETDATE() where MessageId='7dd2de03-7693-4dd2-adbf-093e00b3cf7d';
waitfor delay '00:00:05'
commit tran
然再打开一个会话,编写一个根据主键更新的sql,我们称为sql2吧
update [Messages] set RecordDate=GETDATE() where Id='1326';
这种情况下,按照文中所说,在执行第一个sql后,再执行第二段sql,由于事务中的update语句条件根据非主键更新的,所有导致pagelock,于是,我们在事务提交前,执行第二段SQL,就会处于等待状态。
然后我们在做第二个验证,把事务中的update where条件改成主键,我们称为sql3吧。
begin tran
update [Messages] set RecordDate=GETDATE() where Id='1327'
waitfor delay '00:00:05'
commit tran
同样等待5秒。
然后先执行sql3,启动sql3后,立马执行sql2,看看效果,
直接是一行受影响,这也就验证了我们这篇文章中的观点:"如果在UPDATE语句中有指定的主键,那么就总是会引发行级锁的"。