使用 NOLOCK 消除具有悲观锁定的应用程序的死锁
Posted
技术标签:
【中文标题】使用 NOLOCK 消除具有悲观锁定的应用程序的死锁【英文标题】:Using NOLOCK to eliminate deadlocks for application which has pessimistic locking 【发布时间】:2013-11-09 22:42:38 【问题描述】:使用 SQL Server 数据库处理 Spring-Hibernate 应用程序。我们正在陷入僵局。事务属性是通过 Spring 配置定义的。该应用程序在应用程序代码中实现了悲观锁定。当用户开始处理客户记录时,该记录被锁定。我们努力通过缩短事务、更改事务隔离级别等来消除锁定。
作为替代方案,通过为导致锁定并因此导致死锁的 SQL 指定 NOLOCK 来消除数据库级锁定是否可行(尽管非常规)?
[11/7/13 7:49:44:694 EDT] 00000049 SystemErr R Caused by: org.hibernate.exception.LockAcquisitionException: could not initialize a collection: [com.co.app.domain.Customer.customerBusElemtVals#component[custId,txnId]txnId=11111111, CustomerId=22222222]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:105)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2022)
at org.hibernate.loader.collection.BatchingCollectionInitializer.initialize(BatchingCollectionInitializer.java:75)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:587)
at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:83)
at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1743)
at org.hibernate.collection.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:476)
at org.hibernate.engine.StatefulPersistenceContext.initializeNonLazyCollections(StatefulPersistenceContext.java:867)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:264)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1881)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:71)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:65)
at org.hibernate.loader.entity.BatchingEntityLoader.load(BatchingEntityLoader.java:105)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3072)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:434)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:415)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:165)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:223)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:126)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:905)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:842)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:835)
at org.springframework.orm.hibernate3.HibernateTemplate$1.doInHibernate(HibernateTemplate.java:528)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:419)
at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:522)
at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:516)
at com.co.app.dao.base.GenericHibernateDAO.get(GenericHibernateDAO.java:43)
at com.co.app.dao.hibernate.CustomerDAOImpl.getCustomer(CustomerDAOImpl.java:61)
at com.co.app.dao.helper.FormsDataHelper.getCustomerData(FormsDataHelper.java:103)
at com.co.app.dao.helper.FormsDataHelper$$FastClassByCGLIB$$fb5cf604.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:700)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:635)
at com.co.app.dao.helper.FormsDataHelper$$EnhancerByCGLIB$$414aac28.getCustomerData(<generated>)
at com.co.app.manager.CommonCustomerDataManager.getCustomerData(ComonCustomerDataManager.java:151)
at com.co.app.broker.CommonDataBroker.getCustomerFormsData(CommonDataBroker.java:735)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at flex.messaging.services.remoting.adapters.JavaAdapter.invoke(JavaAdapter.java:421)
at flex.messaging.services.RemotingService.serviceMessage(RemotingService.java:183)
at flex.messaging.MessageBroker.routeMessageToService(MessageBroker.java:1503)
at flex.messaging.endpoints.AbstractEndpoint.serviceMessage(AbstractEndpoint.java:884)
at flex.messaging.endpoints.amf.MessageBrokerFilter.invoke(MessageBrokerFilter.java:121)
at flex.messaging.endpoints.amf.LegacyFilter.invoke(LegacyFilter.java:158)
at flex.messaging.endpoints.amf.SessionFilter.invoke(SessionFilter.java:44)
at flex.messaging.endpoints.amf.BatchProcessFilter.invoke(BatchProcessFilter.java:67)
at flex.messaging.endpoints.amf.SerializationFilter.invoke(SerializationFilter.java:146)
at flex.messaging.endpoints.BaseHTTPEndpoint.service(BaseHTTPEndpoint.java:278)
at flex.messaging.MessageBrokerServlet.service(MessageBrokerServlet.java:322)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1146)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:592)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:525)
at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:90)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:764)
at com.ibm.ws.wswebcontainer.WebContainer.handleRequest(WebContainer.java:1478)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:133)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:458)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:387)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:267)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
at com.ibm.io.async.AsyncChannelFuture$1.run(AsyncChannelFuture.java:205)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
Caused by: java.sql.SQLException: [IBM][SQLServer JDBC Driver][SQLServer]Transaction (Process ID 368) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at com.ibm.websphere.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.ibm.websphere.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.ibm.websphere.jdbc.sqlserver.tds.TDSRequest.processErrorToken(Unknown Source)
at com.ibm.websphere.jdbc.sqlserver.tds.TDSRequest.processReplyToken(Unknown Source)
at com.ibm.websphere.jdbc.sqlserver.tds.TDSRPCRequest.processReplyToken(Unknown Source)
at com.ibm.websphere.jdbc.sqlserver.tds.TDSRequest.processReply(Unknown Source)
at com.ibm.websphere.jdbc.sqlserver.tds.TDSRequest.getRow(Unknown Source)
at com.ibm.websphere.jdbc.sqlserver.SQLServerImplResultSet.fetchAtPosition(Unknown Source)
at com.ibm.websphere.jdbc.base.BaseImplResultSet.next(Unknown Source)
at com.ibm.websphere.jdbc.base.BaseResultSet.next(Unknown Source)
at com.ibm.websphere.jdbcx.base.BaseResultSetWrapper.next(Unknown Source)
at com.ibm.ws.rsadapter.jdbc.WSJdbcResultSet.next(WSJdbcResultSet.java:2509)
at org.hibernate.loader.Loader.doQuery(Loader.java:720)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2015)
... 70 more
[11/7/13 7:49:44:698 EDT] 00000049 SystemErr R at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:105)
[11/7/13 7:49:44:698 EDT] 00000049 SystemErr R at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
[11/7/13 7:49:44:699 EDT] 00000049 SystemErr R at org.hibernate.loader.Loader.loadCollection
11/7/13 13:49:11:605 EDT] 000001c7 SystemErr R at com.ibm.io.async.AsyncChannelFuture$1.run(AsyncChannelFuture.java:205)
[11/7/13 13:49:11:605 EDT] 000001c7 SystemErr R at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
[11/7/13 13:49:11:605 EDT] 000001c7 SystemErr R Caused by: org.hibernate.exception.LockAcquisitionException: could not delete: [com.co.app.domain.CustomerBusElemtVal#component[CustomerId,txnId,busnsElemtId,optnNbr]txnId=22222, optnNbr=99, CustomerId=1111, busElementId=BUSNS_ELEM2]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:105)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2569)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2725)
at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:97)
11/7/13 10:55:36:384 EDT] 00000153 SystemErr R at com.ibm.io.async.AsyncChannelFuture$1.run(AsyncChannelFuture.java:205)
[11/7/13 10:55:36:384 EDT] 00000153 SystemErr R at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
[11/7/13 10:55:36:384 EDT] 00000153 SystemErr R Caused by: org.hibernate.exception.LockAcquisitionException: could not update: [com.co.app.domain.Customer#component[CustomerId,txnId]txnId=2222, CustomerId=1111]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:105)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2453)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
【问题讨论】:
我只使用 nolock 进行只读类型访问。 禁止讨论***.com/questions/686724/… 【参考方案1】:就像你说的那样,这个特定的问题将是非常主观的。只读查询上没有锁定提示可以提高性能,但如果您的数据是事务性的,您将面临获取脏数据的风险。我想说看看已提交的快照或快照隔离,以尝试实现更乐观的锁定方案。确切的答案总是因数据库而异,需要专业分析才能给您 100% 的答案。
【讨论】:
谢谢。将与 DBA 确认快照隔离。【参考方案2】:如果不了解您的系统,无法给您一个明确的答案,但我希望下面的 cmets 可以提供帮助。
我会非常小心使用NOLOCK
,即使是只读数据。如果数据完整性在您的应用程序中很重要,我根本不会使用它。如果你使用NOLOCK
,你也在读取未提交的数据,例如仅由其他进程部分写入的数据,这些进程可能会或可能不会回滚。
虽然您可以避免死锁,但它可能会付出更大的代价。我认为最好通过代码/数据库重新设计和重新编码来尽量消除死锁,并在它们发生时处理偶尔的死锁。
还要注意MSDN 上关于使用NOLOCK
的内容:
对于 UPDATE 或 DELETE 语句:此功能将在 Microsoft SQL Server 的未来版本中删除。避免在新的开发工作中使用此功能,并计划修改当前使用此功能的应用程序。
【讨论】:
谢谢 不知道在未来的版本中,更新或删除的 nolock 将被删除。在此应用程序中,一个用户一次处理特定的客户记录(和相关数据)。除了 nolock,我们还可以通过从中间层代码指定事务隔离级别来做同样的事情。以上是关于使用 NOLOCK 消除具有悲观锁定的应用程序的死锁的主要内容,如果未能解决你的问题,请参考以下文章