postgresql 並發update下導致的死鎖問題


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


 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM