先引入一些概念,直接Copy其他Blogs中的,我就不单独写了。

一、为什么会有锁

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

1.丢失更新

A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统

2.脏读

A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致

3.不可重复读

A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值4.不一致

并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一致

二、锁的种类

共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。

更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。

排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。

意向锁 用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。

架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。

大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。

共享锁

共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。

更新锁

更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。

若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。

排它锁

排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。

意向锁

意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。

意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。

两种由程序员定义的锁

乐观锁:依靠表中数据行内的版本戳或时间戳字段来人工管理锁的工作。

悲观锁:使用数据库或对象上提供的锁机制来处理。

死锁:死锁的意思就是A用户查找表1并获得了S锁,B用户查找表1也获得了S锁,当A用户找到要更新的行申请X锁时被告知B已经有S锁需要等待B解锁,B用户也找到要更新的行申请X锁时被告知A已经有了S锁需要等待A解锁,然后A与B就相互无休止的等待造成死锁。

三、锁的粒度也就是范围

锁粒度是被封锁目标的大小,封锁粒度小则并发性高,但开销大,封锁粒度大则并发性低但开销小

SQL Server支持的锁粒度可以分为为行、页、键、键范围、索引、表或数据库获取锁

RID      行标识符。用于单独锁定表中的一行。

KEY       索引中的行锁。用于保护可串行事务中的键范围。

PAGE 8    千字节 (KB) 的数据页或索引页。

EXTENT     相邻的八个数据页或索引页构成的一组。

TABLE     包括所有数据和索引在内的整个表。

DATABASE  数据库。

锁的粒度和锁的类型都是由SQL Server进行控制的(当然你也可以使用锁提示,但不推荐)。锁会给数据库带来阻塞,因此越大粒度的锁造成更多的阻塞,但由于大粒度的锁需要更少的锁,因此会提升性能。而小粒度的锁由于锁定更少资源,会减少阻塞,因此提高了并发,但同时大量的锁也会造成性能的下降。

四、锁的应用

在使用SQL时,大都会遇到这样的问题,你Update一条记录时,需要通过Select来检索出其值或条件,然后在通过这个值来执行修改操作。

但当以上操作放到多线程中并发处理时会出现问题:某线程select了一条记录但还没来得及update时,另一个线程仍然可能会进来select到同一条记录。

一般解决办法就是使用锁和事物的联合机制:

1. 把select放在事务中, 否则select完成, 锁就释放了
2. 要阻止另一个select , 则要手工加锁, select 默认是共享锁, select之间的共享锁是不冲突的, 所以, 如果只是共享锁, 即使锁没有释放, 另一个select一样可以下共享锁, 从而select出数据

  1. BEGIN TRAN
  2. SELECT * FROM Table WITH(UPDLOCK)
  3. --或者 SELECT * FROM Table WITH(TABLOCKX, READPAST) 具体情况而定。
  4. UPDATE ....
  5. COMMIT TRAN

所有Select加 With (NoLock)解决阻塞死锁,在查询语句中使用 NOLOCK 和 READPAST 
处理一个数据库死锁的异常时候,其中一个建议就是使用 NOLOCK 或者 READPAST 。有关 NOLOCK 和 READPAST的一些技术知识点: 
对于非银行等严格要求事务的行业,搜索记录中出现或者不出现某条记录,都是在可容忍范围内,所以碰到死锁,应该首先考虑,我们业务逻辑是否能容忍出现或者不出现某些记录,而不是寻求对双方都加锁条件下如何解锁的问题。 
NOLOCK 和 READPAST 都是处理查询、插入、删除等操作时候,如何应对锁住的数据记录。但是这时候一定要注意NOLOCK 和 READPAST的局限性,确认你的业务逻辑可以容忍这些记录的出现或者不出现: 
简单来说:

1.NOLOCK 可能把没有提交事务的数据也显示出来
2.READPAST 会把被锁住的行不显示出来

不使用 NOLOCK 和 READPAST ,在 Select 操作时候则有可能报错误:事务(进程 ID **)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。

  1. SELECT * FROM Table WITH(NOLOCK)
  2. SELECT * FROM Table WITH(READPAST)

锁描述:

HOLDLOCK:将共享锁保留到事务完成,而不是在相应的表、行或数据页不再需要时就立即释放锁。HOLDLOCK等同于 SERIALIZABLE。 
NOLOCK 不要发出共享锁,并且不要提供排它锁。当此选项生效时,可能会读取未提交的事务或一组在读取中间回滚的页面。有可能发生脏读。仅应用于 SELECT 语句。 
PAGLOCK:在通常使用单个表锁的地方采用页锁。 
READCOMMITTED:用与运行在提交读隔离级别的事务相同的锁语义执行扫描。默认情况下,SQL Server 2000在此隔离级别上操作。 
READPAST:跳过锁定行。此选项导致事务跳过由其它事务锁定的行(这些行平常会显示在结果集内),而不是阻塞该事务,使其等待其它事务释放在这些行上的锁。 READPAST 锁提示仅适用于运行在提交读隔离级别的事务,并且只在行级锁之后读取。仅适用于 SELECT 语句。 
READUNCOMMITTED:等同于 NOLOCK。 
REPEATABLEREAD:用与运行在可重复读隔离级别的事务相同的锁语义执行扫描。 
ROWLOCK:使用行级锁,而不使用粒度更粗的页级锁和表级锁。 
SERIALIZABLE:用与运行在可串行读隔离级别的事务相同的锁语义执行扫描。等同于 HOLDLOCK。 
TABLOCK:使用表锁代替粒度更细的行级锁或页级锁。在语句结束前,SQL Server 一直持有该锁。但是,如果同时指定 HOLDLOCK,那么在事务结束之前,锁将被一直持有。 
TABLOCKX 使用表的排它锁。该锁可以防止其它事务读取或更新表,并在语句或事务结束前一直持有。 
UPDLOCK:读取表时使用更新锁,而不使用共享锁,并将锁一直保留到语句或事务的结束。UPDLOCK:的优点是允许您读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。 
XLOCK:使用排它锁并一直保持到由语句处理的所有数据上的事务结束时。可以使用 PAGLOCK 或 TABLOCK 指定该锁,这种情况下排它锁适用于适当级别的粒度。

实际开始动手用代码说话吧!

SQLServer2012在查询分析器里面开两个连接

插入锁:

结论:“表锁”锁定对该表的Select、Update、Delete操作,但不影响对该表的Insert操作也不影响以主键Id为条件的Select,所以Select如果不想等待就要在Select后加With(Nolock),但这样会产生脏数据就是其他事务已更新但并没有提交的数据,如果该事务进行了RollBack则取出的数据就是错误的,所以好自己权衡利弊,一般情况下90%以上的Select都允许脏读,只有账户金额相关的不允许。

  1. ------------------A连接 Insert Lock-------------------
  2. BEGIN TRAN
  3. INSERT INTO dbo.UserInfo
  4. ( Name, Age, Mobile, AddTime, Type )
  5. VALUES ( 'eee', -- Name - varchar(50)
  6. 2, -- Age - int
  7. '', -- Mobile - char(11)
  8. GETDATE(), -- AddTime - datetime
  9. 0 -- Type - int
  10. )
  11.  
  12. SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks
  13. WHERE request_session_id=@@SPID
  14. GROUP BY resource_type,request_mode
  15. --ROLLBACK TRAN
  16.  
  17. ------------------------B连接 Insert Lock------------------------
  18. INSERT INTO dbo.UserInfo
  19. ( Name, Age, Mobile, AddTime, Type )
  20. VALUES ( 'fff', -- Name - varchar(50)
  21. 2, -- Age - int
  22. '', -- Mobile - char(11)
  23. GETDATE(), -- AddTime - datetime
  24. 1 -- Type - int
  25. ) --可以执行插入
  26.  
  27. SELECT * FROM dbo.UserInfo --需要等待解锁
  28. SELECT * FROM dbo.UserInfo WHERE Age=1 --需要等待解锁
  29. SELECT * FROM dbo.UserInfo WHERE Id=3 --可以执行查询(根据主键可以)
  30. SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以执行查询(在一个事务中,有更新字段但还没有提交,此时就会查处脏数据)
  31. SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Age=1 --可以执行查询
  32. UPDATE dbo.UserInfo SET Type=5 WHERE Name='fff' --需要等待解锁
  33. DELETE FROM dbo.UserInfo WHERE Name='fff' --需要等待解锁

更新锁:

结论:“表锁”锁定对该表的Select、Update、Delete操作,但不影响对该表的Insert操作也不影响以主键Id为条件的Select

  1. -----------------------A连接 Update Lock-----------------------
  2. BEGIN TRAN
  3. UPDATE dbo.UserInfo SET Name = 'eee' WHERE Age = 2
  4.  
  5. SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks
  6. WHERE request_session_id=@@SPID
  7. GROUP BY resource_type,request_mode
  8.  
  9. --ROLLBACK TRAN
  10.  
  11. ------------------------B连接 Update Lock------------------------
  12. INSERT INTO dbo.UserInfo
  13. ( Name, Age, Mobile, AddTime, Type )
  14. VALUES ( 'ppp', -- Name - varchar(50)
  15. 15, -- Age - int
  16. '', -- Mobile - char(11)
  17. GETDATE(), -- AddTime - datetime
  18. 9 -- Type - int
  19. ) --可以执行插入
  20. SELECT * FROM dbo.UserInfo --需要等待解锁
  21. SELECT * FROM dbo.UserInfo WHERE Name='ppp' --需要等待解锁
  22. SELECT * FROM dbo.UserInfo WHERE Id=3 --可以执行查询(根据主键可以)
  23. SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以执行查询(在一个事务中,有更新字段但还没有提交,此时就会查处脏数据)
  24. SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'ppp' --可以执行查询
  25. UPDATE dbo.UserInfo SET Age=8 WHERE Name='ccc' --需要等待解锁
  26. DELETE dbo.UserInfo WHERE Age = 5 --需要等待解锁

主键锁:

结论:“行锁+表锁” 锁定对该表的Select、Update、Delete操作,但不影响对该表的Insert操作也不影响以主键Id为条件的Select、Update、Delete

  1. ------------------------A连接 Key Lock--------------------
  2. BEGIN TRAN
  3. UPDATE dbo.UserInfo SET Name='hhh' WHERE Id=3 --以主键为条件
  4.  
  5. SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks
  6. WHERE request_session_id=@@SPID
  7. GROUP BY resource_type,request_mode
  8.  
  9. --ROLLBACK TRAN
  10.  
  11. ------------------------B连接 Key Lock----------------------
  12. INSERT INTO dbo.UserInfo
  13. ( Name, Age, Mobile, AddTime, Type )
  14. VALUES ( 'kkk', -- Name - varchar(50)
  15. 18, -- Age - int
  16. '', -- Mobile - char(11)
  17. GETDATE(), -- AddTime - datetime
  18. 7 -- Type - int
  19. ) --可以执行插入
  20. SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以执行查询(在一个事务中,有更新字段但还没有提交,此时就会查处脏数据)
  21. SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'kkk' --可以执行查询
  22.  
  23. -----//全表查询及操作正在处理的行
  24. SELECT * FROM dbo.UserInfo --需要等待解锁
  25. SELECT * FROM dbo.UserInfo WHERE Id=3 --需要等待解锁(根据主键,但与A连接操作相同行不可)
  26. UPDATE dbo.UserInfo SET Name='mmm' WHERE Id=3 --需要等待解锁(根据主键,但与A连接操作相同行不可)
  27. DELETE dbo.UserInfo WHERE Id=3 --需要等待解锁(根据主键,但与A连接操作相同行不可)
  28. -----//使用非主键为条件的操作
  29. SELECT * FROM dbo.UserInfo WHERE Name='aaa' --需要等待解锁(非主键不可)
  30. UPDATE dbo.UserInfo SET Name='ooo' WHERE Name='aaa' --需要等待解锁(非主键不可)
  31. DELETE dbo.UserInfo WHERE Name='aaa' --需要等待解锁(非主键不可)
  32. -----//使用主键为条件的操作
  33. SELECT * FROM dbo.UserInfo WHERE id=1 --可以执行查询(根据主键可以)
  34. UPDATE dbo.UserInfo SET Name='yyy' WHERE Id=1 --可以执行更新(根据主键可以)
  35. DELETE dbo.UserInfo WHERE Id=1 --可以执行删除(根据主键可以)

索引锁:

结论:“行锁+表锁” 锁定对该表的Select、Update、Delete操作,但不影响对该表的Insert操作也不影响以主键Id为条件的Select、Update、Delete,也不影响以索引列Name为条件的Update、Delete但不可以Select

  1. ------------------------A连接 Index Lock--------------------
  2. DROP INDEX dbo.UserInfo.Index_UserInfo_Name
  3. CREATE INDEX Index_UserInfo_Name ON dbo.UserInfo(Name)
  4.  
  5. BEGIN TRAN
  6. UPDATE dbo.UserInfo SET age=66 WHERE Name='ddd' --使用name索引列为条件
  7.  
  8. SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks
  9. WHERE request_session_id=@@SPID
  10. GROUP BY resource_type,request_mode
  11.  
  12. --ROLLBACK TRAN
  13.  
  14. ----------------------B连接 Index Lock-------------------
  15. INSERT INTO dbo.UserInfo
  16. ( Name, Age, Mobile, AddTime, Type )
  17. VALUES ( 'iii', -- Name - varchar(50)
  18. 20, -- Age - int
  19. '', -- Mobile - char(11)
  20. GETDATE(), -- AddTime - datetime
  21. 12 -- Type - int
  22. ) --可以执行插入
  23. SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以执行查询(在一个事物中,有更新字段但还没有提交,此时就会查处脏数据)
  24. SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'kkk' --可以执行查询
  25.  
  26. -----//全表查询及操作正在处理的行
  27. SELECT * FROM dbo.UserInfo --需要等待解锁
  28. SELECT * FROM dbo.UserInfo WHERE Id=4 --需要等待解锁(根据主键,但与A连接操作相同行不可)
  29. UPDATE dbo.UserInfo SET Name='mmm' WHERE Id=4 --需要等待解锁(根据主键,但与A连接操作相同行不可)
  30. DELETE dbo.UserInfo WHERE Id=4 --需要等待解锁(根据主键,但与A连接操作相同行不可)
  31. -----//使用非主键非索引为条件的操作
  32. SELECT * FROM dbo.UserInfo WHERE Age=5 --需要等待解锁(非主键不可)
  33. UPDATE dbo.UserInfo SET Name='ooo' WHERE Age=5 --需要等待解锁(非主键不可)
  34. DELETE dbo.UserInfo WHERE Age=5 --需要等待解锁(非主键不可)
  35. -----//使用主键为条件的操作
  36. SELECT * FROM dbo.UserInfo WHERE Id=1 --可以执行更新(根据主键可以)
  37. UPDATE dbo.UserInfo SET Name='yyy' WHERE Id=1 --可以执行更新(根据主键可以)
  38. DELETE dbo.UserInfo WHERE Id=1 --可以执行删除(根据主键可以)
  39. -----//使用索引为条件的操作
  40. SELECT * FROM dbo.UserInfo WHERE Name='aaa' --需要等待解锁(非主键不可)
  41. UPDATE dbo.UserInfo SET Name='ooo' WHERE Name='aaa' --可以执行更新(根据索引可以)
  42. DELETE dbo.UserInfo WHERE Name='aaa' --可以执行删除(根据索引可以)

悲观锁(更新锁-人工手动设置上锁):

结论:可以理解为在使用版本控制软件的时候A迁出了一个文件,并且将这个文件锁定,B就无法再迁出该文件了,直到A迁入解锁后才能被其他人迁出。

  1. ------------------------A连接 Update Lock(悲观锁)---------------------
  2. BEGIN TRAN
  3. SELECT * FROM dbo.UserInfo WITH(UPDLOCK) WHERE Id=2
  4.  
  5. SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks
  6. WHERE request_session_id=@@SPID
  7. GROUP BY resource_type,request_mode
  8.  
  9. --COMMIT TRAN
  10. --ROLLBACK TRAN
  11.  
  12. ---------------------------B连接 Update Lock(悲观锁)-------------------------
  13. SELECT * FROM dbo.UserInfo --可以执行查询
  14. SELECT * FROM dbo.UserInfo WHERE id=2 --可以执行查询
  15. SELECT * FROM dbo.UserInfo WHERE Name='ooo' --可以执行查询
  16.  
  17. UPDATE dbo.UserInfo SET Age=3 WHERE id=1 --可以执行更新(根据主键可以)
  18. UPDATE dbo.UserInfo SET Age=3 WHERE Name='ccc' --需要等待解锁(非主键不可)
  19.  
  20. DELETE dbo.UserInfo WHERE id=1 --可以执行更新(根据主键可以)
  21. DELETE dbo.UserInfo WHERE name='ccc' --需要等待解锁(非主键不可)

乐观锁(人工通过逻辑在数据库中模拟锁)

结论:可以理解为同样在使用版本控制软件的时候A迁出了一个文件,B也可以迁出该文件,两个人都可以对此文件进行修改,其中一个人先进行提交的时候,版本并没有变化所以可以正常提交,另一个后提交的时候,发现版本增加不对称了,就提示冲突由用户来选择如何进行合并再重新进行提交。

  1. --------------------------A客户端连接 Lock(乐观锁)------------------------
  2. --DROP TABLE Coupon
  3. -----------------创建优惠券表-----------------
  4. CREATE TABLE Coupon
  5. (
  6. Id INT PRIMARY KEY IDENTITY(1,1),
  7. Number VARCHAR(50) NOT NULL,
  8. [User] VARCHAR(50),
  9. UseTime DATETIME,
  10. IsFlag BIT DEFAULT(0) NOT NULL,
  11. CreateTime DATETIME DEFAULT(GETDATE()) NOT NULL
  12. )
  13. INSERT INTO dbo.Coupon(Number) VALUES ( '')
  14. INSERT INTO dbo.Coupon(Number) VALUES ( '')
  15. INSERT INTO dbo.Coupon(Number) VALUES ( '')
  16. INSERT INTO dbo.Coupon(Number) VALUES ( '')
  17. INSERT INTO dbo.Coupon(Number) VALUES ( '')
  18. INSERT INTO dbo.Coupon(Number) VALUES ( '')
  19.  
  20. --SELECT * FROM dbo.Coupon WITH(NOLOCK) --查询数据
  21. --UPDATE Coupon SET [User]=NULL, UseTime=NULL, IsFlag=0 --还原数据
  22.  
  23. -----------------1、模拟高并发普通更新-----------------
  24. DECLARE @User VARCHAR(50) --模拟要使用优惠券的用户
  25. DECLARE @TempId INT --模拟抽选出来的要使用的优惠券
  26. SET @User='a'
  27. BEGIN TRAN
  28. SELECT @TempId=Id FROM dbo.Coupon WHERE IsFlag=0 --高并发时此语句有可能另外一个该事务已取出的Id
  29. --WAITFOR DELAY '00:00:05' --改用此方式要开两个SQL Management客户端
  30. UPDATE dbo.Coupon SET IsFlag=1, [User]=@User, UseTime=GETDATE() WHERE Id=@TempId
  31. COMMIT TRAN
  32. --ROLLBACK TRAN
  33.  
  34. -----------------2、悲观锁解决方案-----------------
  35. DECLARE @User VARCHAR(50) --模拟要使用优惠券的用户
  36. DECLARE @TempId INT --模拟抽选出来的要使用的优惠券
  37. SET @User='a'
  38. BEGIN TRAN
  39. SELECT @TempId=Id FROM dbo.Coupon WITH(UPDLOCK) WHERE IsFlag=0 --高并发时此语句会锁定取出的Id数据行
  40. --WAITFOR DELAY '00:00:05' --改用此方式要开两个SQL Management客户端
  41. UPDATE dbo.Coupon SET IsFlag=1, [User]=@User, UseTime=GETDATE() WHERE Id=@TempId
  42. COMMIT TRAN
  43. --ROLLBACK TRAN
  44.  
  45. -----------------3、乐观锁解决方案-----------------
  46. ALTER TABLE dbo.Coupon ADD RowVer ROWVERSION NOT NULL --增加数据行版本戳类型字段(微软新推荐数据字段,该字段每张表只能有一个,会在创建行或更新行时自动进行修改无需人为干涉,该字段不能建立索引及主键因为会频繁修改)
  47.  
  48. DECLARE @User VARCHAR(50) --模拟要使用优惠券的用户
  49. DECLARE @TempId INT --模拟抽选出来的要使用的优惠券
  50. DECLARE @RowVer BINARY(8) --抽选出来的优惠券的版本(ROWVERSION数据类型存储大小为8字节)
  51. SET @User='a'
  52.  
  53. BEGIN TRY
  54. BEGIN TRAN
  55. SELECT @TempId=Id, @RowVer=RowVer FROM dbo.Coupon WHERE IsFlag=0 --取出可用的Id及对应的版本戳
  56. --WAITFOR DELAY '00:00:05' --改用此方式要开两个SQL Management客户端
  57. UPDATE dbo.Coupon SET IsFlag=1, [User]=@User, UseTime=GETDATE() WHERE Id=@TempId AND RowVer=@RowVer
  58. IF(@@ROWCOUNT > 0)
  59. BEGIN
  60. PRINT('修改成功')
  61. COMMIT TRAN
  62. END
  63. ELSE
  64. BEGIN
  65. PRINT('该数据已被其他用户修改')
  66. ROLLBACK TRAN
  67. END
  68. END TRY
  69. BEGIN CATCH
  70. ROLLBACK TRAN
  71. END CATCH
  72.  
  73. --------------------------B客户端连接 Lock(乐观锁)------------------------
  74. --此测试需要开两个SQL Management Studio客户端,在A客户端使用WAITFOR DELAY来模拟并发占用,在B客户端执行与A客户端相同的SQL脚本即可(注释掉WAITFOR),所以在此不放相同代码了。

在乐观锁和悲观锁之间进行选择的标准是:冲突的频率与严重性。如果冲突很少,或者冲突的后果不会很严重,那么通常情况下应该选择乐观锁,因为它能得到更好的并发性,而且更容易实现。但是,如果冲突的结果对于用户来说痛苦的,那么就需要使用悲观策略。

我认为如果同一张表的并发很高,但并发处理同一条数据的冲突几率很低,那就应该使用乐观锁,反之,如果同一张表的并发不高,但同时处理同一条数据的几率很高,就应该使用悲观锁。

SQL Server 锁机制 悲观锁 乐观锁 实测解析的更多相关文章

  1. sql server对并发的处理-乐观锁和悲观锁

    https://www.cnblogs.com/dengshaojun/p/3955826.html sql server对并发的处理-乐观锁和悲观锁 假如两个线程同时修改数据库同一条记录,就会导致后 ...

  2. [数据库锁机制] 深入理解乐观锁、悲观锁以及CAS乐观锁的实现机制原理分析

    前言: 在并发访问情况下,可能会出现脏读.不可重复读和幻读等读现象,为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念.数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务 ...

  3. sql server对并发的处理-乐观锁和悲观锁(转)

    假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题. 例如: 一个售票系统有一个余票数,客户端每调用一次出票方法,余票数就减一. 情景: 总共300张票,假设两个售票 ...

  4. sql server对并发的处理-乐观锁和悲观锁【粘】

    假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题. 例如: 一个售票系统有一个余票数,客户端每调用一次出票方法,余票数就减一. 情景: 总共300张票,假设两个售票 ...

  5. mysql的锁机制,以及乐观锁,悲观锁,以及热点账户余额问题

    mysql的简单锁机制. myisam 1.只支持表级锁,所以经常更新的表结构不适宜用. 2.select也会产生锁表 innodb 1.支持事务,行级锁,表级锁,执行行级锁的前提是sql语句的索引有 ...

  6. Mysql锁机制--悲观锁和乐观锁

    1. 悲观锁简介 悲观锁(Pessimistic Concurrency Control,缩写PCC),它指的是对数据被外界修改持保守态度,因此,在整个数据处理过程中, 将数据处于锁定状态.悲观锁的实 ...

  7. 谈谈mysql的悲观和乐观锁

    悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.之前有写过一篇文章关于并发的处理思路和解决方案,这里我单独将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍一 ...

  8. 在SQL Server里为什么我们需要更新锁

    今天我想讲解一个特别的问题,在我每次讲解SQL Server里的锁和阻塞(Locking & Blocking)都会碰到的问题:在SQL Server里,为什么我们需要更新锁?在我们讲解具体需 ...

  9. SQL SERVER错误:已超过了锁请求超时时段。

    问题:远程连接数据库,无法打开视图,报错:SQL SERVER错误:已超过了锁请求超时时段. (Microsoft SQL Server,错误: 1222) 执行语句获取进程id select * f ...

  10. 通过DBCC Page查看在SQL Server中哪行数据被锁住了?

    原文:通过DBCC Page查看在SQL Server中哪行数据被锁住了? 如何查看被锁的是哪行数据?通过dbcc page可以. 要想明白这个问题: 首先,需要模拟阻塞问题,这里直接模拟了阻塞问题的 ...

随机推荐

  1. JQuery的动态加载class无法实现点击时间的解决方案

    //对于 加载过来class 的del_a 实现点击事情 $(document).on('click',".del_a",function(){ $(".mark_id& ...

  2. C# 获取exe、dll中的图标,支持获取256x256分辨率

    在网上找过许多文章,都没有成功获取过大图标,只能获取最大32x32.最后自己尝试了相关的windows api,终于找到一个可用的. 主要用到的C++的PrivateExtractIcons函数,具体 ...

  3. 在STEP7 TIA PORTAL中,设置模块的地址和设备名(Device name)

    assign device name, ip address for PROFINET componet in TIA Portal 方法1: PLC --> online & diag ...

  4. easyUI创建人员树

    最近做了一个树状的下拉列表,在这里记录一下,以后可以直接使用 项目中的树状下拉列表是用来选择人员用的,具体实现展示如下: 先说一说功能,左边的人员数是提供选人的,当点击中间的按钮,选中的人员会直接移到 ...

  5. POJ 1236 tarjan

    Network of Schools Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 19613   Accepted: 77 ...

  6. my new day in CNblog

    感谢大家 今天正式在博客园平台开启我的第三个技术面博客 之前一直坚持在csdn平台撰文(http://blog.csdn.net/github_38885296)欢迎参观:) 因为觉得博客园知名度虽不 ...

  7. 《Java程序设计》终极不改版

     半年前的作品,上传只为纪念~ 成绩: ____0.1______ Java程序设计  课程设计 题 目:大学生信息管理系统 学 院:  计算机与软件学院 专 业:     网络工程_____­ .  ...

  8. 团队作业9——Beta版本展示博客

    一. 骆杰宁(组长) 风格:少说话,多做事. 擅长技术:Jsp 编程兴趣:GUI 希望角色:PM 一句话宣言:年轻是本钱,不努力就不值钱. 胡丹丹 风格:不断沉淀自己 擅长技术:擅长TCP/IP协议模 ...

  9. 201521123054《Java程序设计》第8周学习总结

    1. 本周学习总结 2. 书面作业 List中指定元素的删除(题目4-1) 1.1 实验总结 每次删除时下标需要-1:原理如图 统计文字中的单词数量并按出现次数排序(题目5-3) 2.1 伪代码(简单 ...

  10. 201521123100 《Java程序设计》 第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代 ...