SqlServer中的锁

一. 为什么要引入锁

多个用户同时对数据库的并发操作时会带来以下数据不一致的问题。

二、锁的分类

(1)从程序员的角度看

锁分为以下两种类型:
* 乐观锁(Optimistic Lock)
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号等于数据库表当前版本号,则予以更新,否则认为是过期数据。

百度百科
* 悲观锁(Pessimistic Lock)
悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

百度百科

(2) 从数据库系统的角度来看

锁分为以下三种类型:
* 独占锁(Exclusive Lock)(排它锁)
独占锁锁定的资源只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受。执行数据更新命令,即INSERT、 UPDATE 或DELETE 命令时,SQL Server 会自动使用独占锁。但当对象上有其它锁存在时,无法对其加独占锁。独占锁一直到事务结束才能被释放。
* 共享锁(Shared Lock)
共享锁锁定的资源可以被其它用户读取,但其它用户不能修改它。在SELECT 命令执行时,SQL Server 通常会对对象进行共享锁锁定。通常加共享锁的数据页被读取完毕后,共享锁就会立即被释放。
* 更新锁(Update Lock)
更新锁是为了防止死锁而设立的。当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新锁换为独占锁。但当对象上有其它锁存在时,无法对其作更新锁锁定。

三、sqlserver提供的表级锁

sqlserver所指定的表级锁定提示有如下几种 

1. HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放所添加的锁。

2. NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可能读到未提交读的数据或“脏数据”,这个选项仅仅应用于SELECT语句。

3. PAGLOCK:指定添加页锁(否则通常可能添加表锁)

4. READCOMMITTED用与运行在提交读隔离级别的事务相同的锁语义执行扫描。默认情况下,SQL Server 2000 在此隔离级别上操作。

5. READPAST: 跳过已经加锁的数据行,这个选项将使事务读取数据时跳过那些已经被其他事务锁定的数据行,而不是阻塞直到其他事务释放锁,READPAST仅仅应用于READ COMMITTED隔离性级别下事务操作中的SELECT语句操作

6. READUNCOMMITTED:等同于NOLOCK。

7. REPEATABLEREAD:设置事务为可重复读隔离性级别。

8. ROWLOCK:使用行级锁,而不使用粒度更粗的页级锁和表级锁。

9. SERIALIZABLE:用与运行在可串行读隔离级别的事务相同的锁语义执行扫描。等同于 HOLDLOCK。

10. TABLOCK:指定使用表级锁,而不是使用行级或页面级的锁,SQL Server在该语句执行完后释放这个锁,而如果同时指定了HOLDLOCK,该锁一直保持到这个事务结束。

11. TABLOCKX:指定在表上使用排它锁,这个锁可以阻止其他事务读或更新这个表的数据,直到这个语句或整个事务结束。

12. UPDLOCK :指定在读表中数据时设置更新锁(update lock)而不是设置共享锁,该锁一直保持到这个语句或整个事务结束,使用UPDLOCK的作用是允许用户先读取数据(而且不阻塞其他用户读数据),并且保证在后来再更新数据时,这一段时间内这些数据没有被其他用户修改

SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除

SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除

四、sql (server) 行锁,表锁案例

--设table1(A,B,C) 
A B C 
a1 b1 c1 
a2 b2 c2 
a3 b3 c3

1)排它锁 tablockx

新建两个连接
在第一个连接中执行以下语句

begin tran 
update table1 set A='aa' where B='b2' 
waitfor delay '00:00:30' --等待30秒 
commit tran

在第二个连接中执行以下语句

begin tran 
select * from table1 where B='b2' 
commit tran 

若同时执行上述两个语句,则select查询必须等待update执行完毕才能执行即要等待30秒

2)共享锁

在第一个连接中执行以下语句

begin tran 
select * from table1 holdlock -holdlock  人为加锁 
where B='b2' 
waitfor delay '00:00:30' --等待30秒 
commit tran 

在第二个连接中执行以下语句

begin tran 
select A,C from table1 where B='b2' 
update table1 set A='aa' where B='b2' 
commit tran

若同时执行上述两个语句,则第二个连接中的select查询可以执行
而update必须等待第一个事务释放共享锁转为排它锁后才能执行 即要等待30秒

3)死锁

--增设table2(D,E) 
D E 
d1 e1 
d2 e2

在第一个连接中执行以下语句

begin tran 
update table1 set A='aa' where B='b2' 
waitfor delay '00:00:30' 
update table2 set D='d5' where E='e1' 
commit tran

在第二个连接中执行以下语句

begin tran 
update table2 set D='d5' where E='e1' 
waitfor delay '00:00:10' 
update table1 set A='aa' where B='b2' 
commit tran

同时执行,系统会检测出死锁,并中止进程

 

参考:

https://www.php.cn/mysql-tutorials-123100.html

https://blog.csdn.net/softuse/article/details/121940804

 

posted on 2022-06-10 23:23  静以修身俭以养德  阅读(822)  评论(0编辑  收藏  举报

导航