zoukankan      html  css  js  c++  java
  • SQL Server 隐式转换引发的死锁

    在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍由于隐式数据类型转换而造成的死锁及相应解决方案.

    现实中有些程序员/数据库开发者会根据数据库的处理机制实现一些应用,如抢座应用,可能会对事务中的查询加一些列的Hint以细化粒度,实现应用的同时使得影响最低,但也有可能因为一些小细节的欠缺而引发错误,从而造成糟糕的用户体验.如下面这个例子

    生成测试数据

    code

    01.create table testlock
    02.(ID varchar(10) primary key clustered,
    03.col1 varchar(20),
    04.col2 char(200))
    05.go----------create test table
    06. 
    07.declare @i int
    08.set @i = 1
    09.while @i < 100
    10.begin
    11.insert into testlock
    12.select right(replicate('0',10)+ cast(@i as varchar(10)),10),'aaa','fixchar'
    13.set @i = @i+1
    14.end
    15.go----------generate test data

    此时我们打开trace profiler 跟踪死锁相关信息

    然后分别在两个session中运行如下语句

    code

    01.declare @ID nvarchar(10)
    02. 
    03.begin tran 
    04. 
    05.select  top 1 @ID = ID from testlock with(updlock, rowlock, readpast)
    06.where col1 = 'aaa'
    07.order by id asc
    08. 
    09.select  @ID
    10. 
    11.waitfor delay '00:00:20'
    12. 
    13.update testlock set col1 = 'bbb' where id = @ID
    14. 
    15.commit tran

    大约20s后我们可以从trace 中捕捉到死锁了如图1-1

                                                                          

    index1.png

    问题分析

    从死锁图中看既然更新既然拥有了自己的键锁为何要其它会话的呢?很明显,可能期望的锁粒度扩大了.

    进而分析任意一个会话的执行计划语句发现了异常,最后的更新出现了隐式数据类型转换,以至于做了额外的聚集表扫描过程,致使执行更新过程需要所有键的U锁,从而引发了死锁.

    如图1-2

                                        

    index2.png

    为什么会出现隐式转换呢,通过检查执行的代码发现"declare @ID nvarchar(10)"

     而表testlock中ID的定义是varchar(10) 问题就出在这里.

    这里介绍一个小的知识点:数据类型优先级

    当运算符表达式中数据类型不同时,按照类型的优先级低优先级的向高优先级的数据类型转换.当然如果两个数据类型不支持隐式转换则失败报错.

    通过数据类型优先级列表发现nvarchar是高于varchar的,所以varchar将向nvarchar转换,进而使优化器选择了意料之外的执行计划,从而引发了死锁

    如图1-3

             

    index4.png

    详细参考

    https://msdn.microsoft.com/zh-cn/library/ms190309.aspx

    解决

    找到问题的根源了,解决起来也就简单了,我们只需将查询中定义的declare @ID nvarchar(10)

    调整为varchar即可(甚至char,通过优先级列表可知,char低于varchar.)

    code

    01.declare @ID varchar(10)
    02. 
    03.begin tran 
    04. 
    05.select  top 1 @ID = ID from testlock with(updlock, rowlock, readpast)
    06.where col1 = 'aaa'
    07.order by id asc
    08. 
    09.select  @ID
    10. 
    11.waitfor delay '00:00:20'
    12. 
    13.update testlock set col1 = 'bbb' where id = @ID
    14. 
    15.commit tran

    我们可以看到相应的执行计划发生了改变,我们期待的执行计划出现了.如图1-4

                            

    index5.png

    至此,问题解决.

    注意:虽然有数据优先级,但建议大家在做开发时,定义的变量要与目标表的数据类型一致,从根源上避免隐式转换.

    结语:一个小小的字符当真是可以引发血案,在做应用开发中我们需要知道每个字符的深刻含义.

  • 相关阅读:
    应用系统之间传输数据的几种方式
    解决
    springmvc httprequest 使用@Autowired注解
    JVM client模式和Server模式的区别
    延时接通电路
    C语言中getch()、getche()和getchar()
    结构体
    五:分布式事务一致性协议paxos的应用场景
    四:分布式事务一致性协议paxos通俗理解
    三:分布式事务一致性协议2pc和3pc
  • 原文地址:https://www.cnblogs.com/luozhai714/p/4378558.html
Copyright © 2011-2022 走看看