SQL Server 幻读 的真实案例

数据库中有表[01_SubjectiveScoreInfo],要实现表中的数据只被查出一次,此表数据量较大,有三四百万数据。表结构也确实不是很合理,无法修改表结构,即使是新增一个字段也会有相当大的修改量。

因之前代码中存在大量的insert into select *的语句,加一个字段什么也不做也会导致整个项目瘫痪,当然我不想去讨论前人的代码质量。

于是乎我加了一个新表[01_SubjectiveScoreInfoFlag]来进行记录取过的记录ID。于是就有了如下的代码:

BEGIN TRAN
                    
                    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
                    
                    INSERT INTO [01_SubjectiveScoreInfoFlag](ID)
                    SELECT TOP 100 SS.ID
                    FROM [01_SubjectiveScoreInfo] AS SSINNER JOIN SubjectiveItemInfo AS SI                         
ON SS.TestCode=SI.TestCode AND SS.MajorQuestionID=SI.MajorQuestionID AND SS.MinorQuestionID=SI.MinorQuestionID WHERE SS.TestCode=''' + @TestCode + ''' AND SI.QuestionGroupCode=''' + @QuestionGroupCode + ''' AND (SI.MinorQuestionCount=0 OR SI.MinorQuestionID>0) AND NOT EXISTS ( SELECT TOP 1 1 FROM [01_SubjectiveScoreInfoFlag] WHERE ID = SS.ID ) COMMIT TRAN

此处用到了事务,并且指定了隔离级别。这个是必须的,我们之前就是没有指定,单线程无论你怎么调用这段代码运行都非常可靠,但多线程模拟并发调用时就出现了非常严重的重复。

上面的代码也并没有解决问题,原因是锁用得不对,多线程直接就出现了死锁现象。因之前对于隔离级别没什么经验,我一度曾经以为这个问题是无解的,直到今天突然被解开了。

按照我自己的理解,是因为锁的级别不够,导致了资源争抢。

就相当于上厕所时,一定要获取完全的排它锁,关好门不让其它人进来;否则如果其它人进来了,虽然他没有占到位子,但他拿走了手纸。你占着位子却没有手纸,他拿着手纸却没有位子,双方互不相让,谁也无法完成上厕所的事务,相持不下,进而导致死锁。这时就需要厕所管理出面,要么强制让你让出位子走人,不管理你擦没擦屁股;要么抢来手纸赶走他,任他拉到裤子上。

于是乎就有了下面的代码:

BEGIN TRAN
                    
                    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
                    
                    INSERT INTO ['+@CourseID+'_SubjectiveScoreInfoFlag](ID)
                    SELECT TOP 100 SS.ID
                    FROM ['+@CourseID+'_SubjectiveScoreInfo] AS SS WITH(UPDLOCK)
                      INNER JOIN SubjectiveItemInfo AS SI WITH(UPDLOCK)
                        ON SS.TestCode=SI.TestCode 
                          AND SS.MajorQuestionID=SI.MajorQuestionID 
                          AND SS.MinorQuestionID=SI.MinorQuestionID
                    WHERE SS.TestCode=''' + @TestCode + '''    
                      AND SI.QuestionGroupCode=''' + @QuestionGroupCode + '''   
                      AND (SI.MinorQuestionCount=0 
                        OR SI.MinorQuestionID>0)
                      AND NOT EXISTS
                      (
                        SELECT TOP 1 1 
                        FROM ['+@CourseID+'_SubjectiveScoreInfoFlag] WITH(UPDLOCK)
                        WHERE ID = SS.ID
                      )
                    
                COMMIT TRAN

UPLOCK是排它锁,这样就没有死锁了。

 

转载于:https://www.cnblogs.com/nanfei/p/6265279.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值