数据库高并发情况之两个线程同时执行update语句

时间:2024-04-07 21:00:30

参考的原贴博客地址:https://blog.csdn.net/starseeker7/article/details/28632773

昨天遇到做一个笔试题,被自己菜枯了。遇到了这种数据库并发的题,我之前竟然没有见过。。。还是要脚踏实地才能找到好工作啊。 废话不多说,遇到的题目和网上的题目大同小异。所以我就直接用网上的题目了。

首先建立数据库表并且添加若干条数据:

CREATE TABLE [dbo].[table1](

 [A] [nvarchar](10) NULL,

 [B] [nvarchar](10) NOT NULL,

 [C] [nvarchar](10) NULL

) ON [PRIMARY]

GO

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa1', N'b1', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa2', N'b3', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b4', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b5', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b2', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b6', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b7', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b8', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa1', N'b9', N'11')
 

下面三个script:

script1:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

 begin tran

    print convert(nvarchar(30),convert(datetime,getdate(),121),121)

       update table1

     set A='aa1'

     where B='b3'

    print convert(nvarchar(30),convert(datetime,getdate(),121),121)

    EXEC sp_lock @@spid

   waitfor  delay '00:00:10'

    update table1

     set A='aa2'

     where B='b8'

     EXEC sp_lock @@spid

    print convert(nvarchar(30),convert(datetime,getdate(),121),121)

 commit tran
因为要模拟高并发的情况,所以这里用的waitfor 阻塞一个查询,10s后再执行下一个update

 

script2:

SET TRANSACTION ISOLATION LEVEL Read UNCOMMITTED

    begin tran

  update table1

    set A='aa3'

    where B='b7'

    EXEC sp_lock @@spid

   commit tran
 

script3:

SET TRANSACTION ISOLATION LEVEL Read UNCOMMITTED

    begin tran

  update table1

    set A='aa3'

    where B='b1'

 

    EXEC sp_lock @@spid

   commit tran
 

 

场景1:先运行script1,然后立刻运行script2,  结果是顺利执行,完成更新操作

场景2:先运行script1,然后立刻运行script3,结果是出现死锁情况

---------------------------------------------------------------------------------------------

下面我们来分析为什么会有这个现象,首先我们先来看看update动作到底会做哪些事.

我们先开启profiler,监控lock:acquired以及lock:released两个项目,并且限制suid为我们需要监控的进程ID,然后我们得到下面的监控结果

数据库高并发情况之两个线程同时执行update语句

我们可以得出这样一个结论:

Update过程中对表进行扫描依此对每行记录下U锁,若满足条件则转换为X锁更新,若不满足条件则释放U锁。由此结论,我们就可以推断出上面分别导致死锁,和不会导致死锁的情况是怎样的原因了。

场景1:

script1:对表进行扫描依此对每行记录下U锁,若满足条件则转换为X锁更新,若不满足条件则释放U锁。更新完成后,若未提交则X锁继续保留。

注意:这里被保留的X锁的行数为第2行(b3),因为这个是一个堆表,排列顺序一般是按照插入顺序,update扫描该表时候从从第一开始扫描的


script2:对表进行扫描一次对每行记录下U锁,因为需要查询的目标在script1的X锁之后,未能查询到需要更新得条件既被发生无法添加U锁等待,这样只有当script1完成查询后,script2才能继续,所以不会导致死锁。

注意:正因为script2要满足X锁的条件在第7行以(b7),所以当script2的U锁获取再释放动作,到了第二行的时候,遇到script1保留的X锁,整个事务进入等待状态,并且未留下任何可以干扰script1的第二个语句的锁,所以,当script1直接完毕后,script2得以继续正常执行完成

 

情况2:

script1:对表进行扫描依此对每行记录下U锁,若满足条件则转换为X锁更新,若不满足条件则释放U锁。更新完成后,若未提交则X锁继续保留。

script3:对表进行扫描依此对每行记录下U锁,因为需要update的条件在script1前就被发现并加上X锁,但是扫描到script1锁添加的X锁后无法添加U锁导致需要等待script1完成后才能继续提交。这时script1的第二个update语句开始运行,更新U锁时,发现script2的X,要等待script3的X锁释放同时达成死锁条件。

注意:正因为script3的添加X锁动作,在第一行就发生了(b1),然后达到第二行是,又被script1保留的X锁阻止了继续操作第二行(b3)。这样他就需要等待script1释放在第二行的X锁才能继续update,但是script1的第二个语句,又需要script3先释放在第一行(b1)的X锁,就这样形成了死锁条件。

 

如此这个在 同一页面,不同的行的互相死锁,和不死锁的原因就相当清楚了。

这个案例重点就是,update是一个两个动作的事务,分为查询和修改,并且修改这个动作是通过对各个满足条件的行做X锁来保证一致性的

而这个动作当遇到并发的时候,即使是不同的行也可能导致互相死锁。