postgresql 并发update下导致的死锁问题

一、死锁问题背景

在收据批量打印时,由于采用异步并发触发打印,同时触发打印(九千多数据 每隔50ms触发一次),导致了并发执行引起在接口更新打印次数时postgresql发生死锁问题,

具体报错如下:

 1 ### The error occurred while setting parameters
 2 ### SQL: update t_sc_receipt set print_num = coalesce(print_num,0) + 1                       ,print_ts=?
 3 ### Cause: org.postgresql.util.PSQLException: ERROR: deadlock detected
 4   详细:Process 19540 waits for ShareLock on transaction 12520113; blocked by process 19539.
 5 Process 19539 waits for ShareLock on transaction 12520112; blocked by process 19540.
 6   建议:See server log for query details.
 7   在位置:while rechecking updated tuple (329,3) in relation "t_sc_receipt"
 8 ; SQL []; ERROR: deadlock detected
 9   详细:Process 19540 waits for ShareLock on transaction 12520113; blocked by process 19539.
10 Process 19539 waits for ShareLock on transaction 12520112; blocked by process 19540.
11   建议:See server log for query details.
12   在位置:while rechecking updated tuple (329,3) in relation "t_sc_receipt"; nested exception is org.postgresql.util.PSQLException: ERROR: deadlock detected
13   详细:Process 19540 waits for ShareLock on transaction 12520113; blocked by process 19539.
14 Process 19539 waits for ShareLock on transaction 12520112; blocked by process 19540.
15   建议:See server log for query details.
16   在位置:while rechecking updated tuple (329,3) in relation "t_sc_receipt"
17     at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:263)
18     at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
19     at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
20     at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368)
21     at com.sun.proxy.$Proxy57.update(Unknown Source)
22     at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:254)
23     at com.xxx.framework.mybatis.dao.impl.MyBatisDaoImpl.updateBySql(MyBatisDaoImpl.java:531)
24     at com.xxx.dscsettle.receipt.ReceiptService.updatePrintNum(ReceiptService.java:160)
25     at com.xxx.dscsettle.receipt.ReceiptService$$FastClassBySpringCGLIB$$82e91731.invoke(<generated>)
26     at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
27     at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:651)
28     at com.xxx.dscsettle.receipt.ReceiptService$$EnhancerBySpringCGLIB$$67b6a353.updatePrintNum(<generated>)
29     at com.alibaba.dubbo.common.bytecode.Wrapper72.invokeMethod(Wrapper72.java)
30     at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
31     at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
32     at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
33     at com.onlyou.framework.log.DubboLogServiceFilter.invoke(DubboLogServiceFilter.java:28)
34     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
35     at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:64)
36     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
37     at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
38     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
39     at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
40     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
41     at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
42     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
43     at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:61)
44     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
45     at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132)
46     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
47     at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
48     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
49     at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
50     at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69)
51     at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:98)
52     at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:98)
53     at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)
54     at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
55     at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:81)
56     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
57     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
58     at java.lang.Thread.run(Thread.java:745)
59 Caused by: org.postgresql.util.PSQLException: ERROR: deadlock detected
60   详细:Process 19540 waits for ShareLock on transaction 12520113; blocked by process 19539.
61 Process 19539 waits for ShareLock on transaction 12520112; blocked by process 19540.
62   建议:See server log for query details.

二、原因分析

从报错的提示我们知道了在数据库postgresql发生了死锁(ERROR: deadlock detected  侦测到了死锁发生),而且可以定位是在并发更新打印次数的时候发生的,正常的逻辑下,分页去不断更新收据的打印次数应该是不会出错的,在这边批量更新打印次数时出现了错误。

1.死锁是由于资源的相互竞争引起的,在update数据的时候应该是数据库行锁导致的

2.因为批量修改是一个默认的事务,所以如果没有全部修改完,索引是不会被放开的,所以才会在并发的多次访问中出现死锁,

3.研究出现问题的原因,发现最可能得是重复的行锁导致的,最终才查出原因是因为代码逻辑的原因,未传更新idList,而sql当中又进行了空的判断,导致每次都去更新全部的表数据,由于不同事务直接的相互等待,得不到资源,导致了死锁。

 

三、问题解决与拓展

批量更新的死锁解决方案有以下两种

1.将批量update通过for循环改成单条修改,但是这个方法对服务器的压力增大

2.在更新数据的时候进行一次筛选,将重复的数据剔除出去

在本次问题解决当中,只需将代码中传入正确的idLIst即可解决问题,因为本身进行迭代是没有进行重复upadate的。

疑问:并发update下postgresql就会出现shareLock死锁吗?

一般情况下的多次update应该不会导致死锁,而在事务当中的update则比较可能发生死锁现象。

同时,也看到了一位博主说postgresql 并发update的死锁问题可能是一些版本出现的bug,以及可能可以进行解决的设置以下两个参数进行解决:

autovacuum_vacuum_scale_factor = 0.03
autovacuum_analyze_scale_factor = 0.03

postgresql 并发update下导致的死锁问题

 

参考地址:http://blog.chinaunix.net/uid-20726500-id-4773950.html
https://blog.51cto.com/372550/2387517

 

 

上一篇:向您生动地讲解Spring AOP 源码(3)


下一篇:【.NET 与树莓派】矩阵按键