具体报错信息如下:
根据查找资料的结果显示sqlserver的死锁,包括两种场景
事务A:delete from table1 where id =(1,..,n); 事务B:select c1,c2,c3 from table1 where id = (1,..,n); table1的索引如下,物理索引id,逻辑索引c1,c2。 |
先执行查询,再执行更新操作,并且此处使用也较为频繁
如何解决呢?
1、根据查找的资料,可以通过SqlServer中的更新锁(UPDLOCK)
UPDLOCK.UPDLOCK 的优点是允许您读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。当我们用UPDLOCK来读取记录时可以对取到的记录加上更新锁,从而加上锁的记录在其它的线程中是不能更改的只能等本线程的事务结束后才能更改.
比如:
用户A读一条记录,然后修改该条记录
这时用户B修改该条记录
这里用户A的事务里锁的性质由共享锁(S)企图上升到独占锁(X)(for update),而用户B里的独占锁(X)由于A有共享锁(S)存在所以必须等A释放掉共享锁(S),而A由于B的独占锁(X)而无法上升的独占锁(X)也就不可能释放共享锁(S),于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
2、系统中还存在一个同类型的业务,医嘱执行,代码的模式也和这种类似,都是属于先查询,再更新的操作。但是运行过程中,此处业务就没有报这个错误。因为该方法上面,添加了synchronized关键字。
将该方法设置为同步方法,用于解决同步问题,当有多条线程同时访问共享数据时,如果不进行同步,就会发生错误,java提供的解决方案是:只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行可以。
按道理来说,第二种方法应该也是能够解决问题的,所以先按照这种方式进行修复,而使用第一种方式进行处理,总感觉怪怪的,可能更适用于存储过程中的写法吧(个人观点)。
参考资料:
java中的synchronized同步代码块和同步方法的区别
【SqlServer】SqlServer中的更新锁(UPDLOCK)
sqlserver.jdbc.SQLServerException: 事务(进程 ID 246)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务
还没有评论,来说两句吧...