SQL-乐观锁,悲观锁之于并发

为什么需要锁(并发控制)?
  在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。
典型的冲突有:
  • 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。
  • 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。
为了解决这些并发带来的问题。 我们需要引入并发控制机制。
 
并发控制机制
  悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。[1]
  乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。
 
悲观锁应用
需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。
Begin Tran
select top 1 @TrainNo=T_NO
from Train_ticket with (UPDLOCK) where S_Flag=0
 
update Train_ticket
set T_Name=user,
T_Time=getdate(),
S_Flag=1
where T_NO=@TrainNo
commit
我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.
 
在此:举个简单的例子来说明悲观锁的应用,我们以SQLServer为例进行说明:
假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题。
例如:
  一个售票系统有一个余票数,客户端每调用一次出票方法,余票数就减一。
情景:
  总共300张票,假设两个售票点,恰好在同一时间出票,它们做的操作都是先查询余票数,然后减一。
一般的sql语句:
declare @num int;
begin tran
select @num = [Num] FROM [Test8].[dbo].[Table_1] with(nolock) where ID = 1
WAITFOR DELAY '00:00:05' --模拟并发,估计延迟5秒
update [Test8].[dbo].[Table_1] set Num =@num -1
 
commit tran
 
 
 
同时执行两个窗口执行这个脚本,最终更新结果有误
 
declare @num int;
begin tran
select @num = [Num] FROM [Test8].[dbo].[Table_1] with(updlock) where ID = 1
WAITFOR DELAY '00:00:05' --模拟并发,估计延迟5秒
update [Test8].[dbo].[Table_1] set Num =@num -1
 
commit tran
 
SELECT TOP 1000 [ID]
,[Num]
,[Title]
FROM [Test8].[dbo].[Table_1]
 
在A窗口执行的时候,B窗口会等待A执行完毕才执行
 
如果这个售票系统并发太高(例如:春节售票,十月一国庆售票等买票人员并发操作很高),我们采用上述的悲观锁方案就会是系统性能大大降低。譬如:售票员A锁定了这个操作,售票员A在处理这个业务的过程中,又去喝了杯咖啡,在此期间,其他业务员是无法进行订票的,所以...
乐观锁解决方案:
新增一列 Timestamp
alter table Table_1 ADD timesFlag timestamp not null
 
declare @num int;
declare @flag timestamp;
declare @rowCount int;
begin tran
select @num = Num,@flag = timesFlag FROM [Test8].[dbo].[Table_1]
waitfor delay '00:00:05'
update [Test8].[dbo].[Table_1] set Num = @num-1 where timesFlag = @flag
set @rowCount = @@ROWCOUNT
 
commit tran
if(@rowcount>0)
print 'OK'
else
print 'Error'
两个窗口同时执行该命令,一个输出 OK,一个输出Error
这便是乐观锁的解决方案,可以解决并发带来的数据错误问题,但不保证每一次调用更新都成功,可能会返回'更新失败'
 
悲观锁和乐观锁
  悲观锁一定成功,但在并发量特别大的时候会造成很长堵塞甚至超时,仅适合小并发的情况。
  乐观锁不一定每次都修改成功,但能充分利用系统的并发处理机制,在大并发量的时候效率要高很多。

 

posted on 2019-03-28 10:15  洋气的名字  阅读(368)  评论(0编辑  收藏  举报