最近接手了公司一个比较老的酒店中央预定系统的对接维护工作,发现订单经常会报sqlserver事务死锁。
于是便决定排查一下倒是是哪里引起了这个问题,
1.查看问题订单:发现问题订单淘宝渠道过来的但携程的订单基本没有这个问题,分析了出问题的订单之后发现淘宝过来出问题的订单经常是统一酒店同一时间多笔订单一起下单时会有死锁问题。
2.检查系统错误日志:发现错误日志都在最后调用.net的ws接口下单时才报的错误(中央预定系统是.net的)。并不是我们这边做的对接渠道的java系统的代码问题。
3:检查.net接口系统:无奈之下只好反编译了.net的代码,发现接口代码中开启事务的只有对订单的insert操作,因为单独的insert操作不可能引发死锁。所以判断这张表上肯定存在触发器,进行了其他操作。于是将问题缩小到了订单表中。
4:查看订单表和sqlserver死锁日志:
从死锁日志,大致分析 线程1先修改完了表a所以拿到了X锁 ,然后线程2也想修改表a的记录所以准备加U锁,但是记录被线程1修改了所以无法加上u锁进入了等待,而线程3想要对记录加S锁进行读取却发现被线程2的IX堵塞了,线程4和线程1也是在同一步select 操作中发生了等待,;
观察订单表发现订单表果然有一个insert的触发器,触发器中首先是对表a进行了update
也就是错误日志中线程2等待的地方,之后便是其他线程等待的查询,
于是得出结论线程1先修改了表a,然后线程2修改表a进行等待,但是线程1却因为线程2的操作进入了等待。查询资料后发现原来sqlserver在insert之后会对表和页添加ix锁,对插入记录添加x锁。
5.错误重现
对表进行insert操作
发现sqlsever在insert 之后会对表和页对象加ix锁,对索引字段的key加x锁,
然后执行刚刚发生死锁的查询
到此发现查询是29行对(293f。。)key添加的s锁 ,正好会和insert 后15行对(293f。。)key添加的x锁,两个key正好是同一个key,而这个key已经加了x锁,页已经加了ix锁。所以发生冲突。死锁日志显示s锁等待ix锁。
6。结果问题
分析:因为老系统代码过于耦合修改困难。在分析过程后得出只要让预定同一酒店,同一房型,同一天的订单。串行化执行。不在表a堆积u锁和X锁。就可以避免死锁发生,避免下订单失败的损失。
结果:对老接口进行重新封装。用redis的setnx命令保证同一时间内调用下单接口:预定同一酒店,同一房型,同一天的订单的请求依次执行:拿到锁的请求才能下单。其他请求进行循环知道拿到redis锁