我正在构建一个适用于SQL服务器的基于java的Web应用程序。
SQL Server的默认数据库隔离级别为READ_COMMITTED。
我得到以下例外:
Cause: org.hibernate.exception.LockAcquisitionException: Transaction (Process ID 124) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
2016-08-18 07:23:36.064 ERROR application
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process ID 124) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:216) ~[com.microsoft.sqlserver.sqljdbc4-4.0.jar:na]
at com.microsoft.sqlserver.jdbc.SQLServerResultSet$FetchBuffer.nextRow(SQLServerResultSet.java:4853) ~[com.microsoft.sqlserver.sqljdbc4-4.0.jar:na]
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.fetchBufferNext(SQLServerResultSet.java:1781) ~[com.microsoft.sqlserver.sqljdbc4-4.0.jar:na]
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.next(SQLServerResultSet.java:1034) ~[com.microsoft.sqlserver.sqljdbc4-4.0.jar:na]
at sun.reflect.GeneratedMethodAccessor25.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_74]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_74]
at org.hibernate.engine.jdbc.internal.proxy.AbstractResultSetProxyHandler.continueInvocation(AbstractResultSetProxyHandler.java:104) ~[org.hibernate.hibernate-core-4.1.9.Final.jar:4.1.9.Final]
... 82 common frames omitted
有人可以提供更多相关信息吗?
答案 0 :(得分:0)
默认情况下,sql server使用悲观锁定。这意味着当在事务中读取或更新行时,它被该事务锁定,因此没有其他事务可以读取或写入它(另一个事务将阻塞,直到第一个事务被提交)
如果有2个事务读取相同数据并且两个事务都锁定了另一个事务正在读取的行,则会发生死锁。
举一个简单的例子: 你有一个名为name和2行的列表:
名称:
富
酒吧
交易1读取Foo(导致其锁定)
交易2读取Bar(导致其锁定)
事务1尝试读取Bar但会阻塞,直到事务2提交
交易2尝试读取Foo但被交易1
您现在遇到死锁,两个事务都在等待另一个事务释放资源。 Sql server有一个内部机制来检测死锁,它会检测死锁并选择一个事务来回滚。具有被选为死锁牺牲品的事务的java线程将返回类似于您的示例的错误。
解决方案: 这里有几个选项
如果可能,请更改您的代码,以便始终以相同的顺序读取读取的行(在上面的示例中,如果两个事务读取foo,则第一个事务2将只等待事务1完成)
接受死锁可以是并发应用程序的一部分,并允许用户重新提交请求或创建一些代码,如果抛出此错误,可以自动重新提交请求。 (不理想,但取决于问题的频率,偶尔出现这些错误有时是务实的)
了解其他锁定策略。 SQL Server支持多种,但它们可以大致分为2类,悲观锁定(在您的示例中为默认锁定和相同)和乐观锁定(有时称为快照隔离)。
乐观锁定允许多个事务在没有锁定的情况下读取同一行,只有写入才需要锁定。两种方法都有利有弊,改变这种方法可以解决问题
在上面的示例中,乐观锁定将解决此问题,因为两个事务只是读取。但是,如果两个事务是插入或更新记录,你可以简单地用乐观锁定来解决问题,如果2个事务尝试更新同一个记录(在两个事务都已读取但都没有提交之后),最后提交的事务会变得乐观锁定错误。
还有许多其他方面需要考虑,还有一些很好的文章可以提供更详细的内容。
https://technet.microsoft.com/en-us/library/jj856598(v=sql.110).aspx
https://msdn.microsoft.com/en-us/library/tcbchxcb(v=vs.110).aspx
答案 1 :(得分:0)
MS SQLServer使用与大多数数据库不同的隔离级别,因此默认的READ_COMMITTED可能会干扰生产负载条件下流程引擎的操作。
以下来自Camunda的文章(它是Activiti引擎的一个分支)建议以下解决方法: