最近項目上出現了很奇怪的一個問題,經過excel模板上傳數據時,導入常常卡死在最後保存數據的時候,過了會兒顯示保存失敗。經過日誌裏面能夠發現報的異常以下,很明顯是鎖表了。java
1 java.sql.BatchUpdateException: Deadlock found when trying to get lock; try restarting transaction 2 at com.mysql.jdbc.StatementImpl.handleExceptionForBatch(StatementImpl.java:1448) 3 at com.mysql.jdbc.PreparedStatement.executePreparedBatchAsMultiStatement(PreparedStatement.java:1585) 4 at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1463) 5 at com.epoint.dao.util.PreStatementBatch.executeFinal(PreStatementBatch.java:177) 6 at com.epoint.datacenter.controller.resource.datadomain.zjgResourceImportHandler.saveSheet(zjgResourceImportHandler.java:558) 7 at com.epoint.datacenter.controller.resource.datadomain.zjgResourceImportHandler$3.saveRow(zjgResourceImportHandler.java:108) 8 at com.epoint.basic.faces.dataimport.excel.g.run(mo:74) 9 at java.lang.Thread.run(Thread.java:745) 10 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction 11 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 12 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 13 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 14 at java.lang.reflect.Constructor.newInstance(Constructor.java:422) 15 at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 16 at com.mysql.jdbc.Util.getInstance(Util.java:386) 17 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1066) 18 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4190) 19 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4122) 20 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:927) 21 at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2399) 22 at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2789) 23 at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2818) 24 at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2157) 25 at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1379) 26 at com.mysql.jdbc.PreparedStatement.executePreparedBatchAsMultiStatement(PreparedStatement.java:1583) 27 ... 6 more
而後嘗試對不通的庫進行復現,發現有的導入能成功,有的導入就報死鎖,這就奇怪了,數據庫都是mysql,採用的是集羣部署,都在同一個服務器上,按理要有問題應該都有問題纔對。而後看了下sql的執行計劃,發現死鎖報錯出如今delete語句上。因而看了下源碼,框架裏面導入是採用了批處理,測試了下,批處理報錯事務並無回滾。本覺得把最後要執行的delete語句註釋了,能成功解決問題,可是事實證實,並無解決問題。此次再看SQL執行計劃,發現死鎖出如今update語句上,此次我便確認並非刪除語句的問題。mysql
排查了一遍excel中數據問題,發現導入的數據也沒有對同一條數據進行更新和刪除操做的,更加不會鎖表了。爲了解決問題,無奈把線程數從5000提高至10000,這時候再次導入的確是成功了,可是這太消耗服務器性能了,一共纔不到10000的數據量,居然開了一萬個線程去執行,過低效了。最後去諮詢了下DBA,經過監控數據庫操做發現,竟然是刪改操做沒有走索引致使的。這裏又學到了,沒索引會從第一行開始掃描直到找到數據位置,掃描過程會加鎖,發現數據不是目標會再釋放鎖,delete和update是必須走索引的。sql
這邊因爲歷史遺留問題,jar包中的sql語句寫死了主鍵爲RowGuid,而我在實際操做過程當中已經將數據庫的主鍵進行了變動,因此上傳按原sql去執行的時候發現RowGuid不是主鍵了而進行了鎖表掃描。以前導入能成功是由於只執行了insert操做,因此沒有涉及到鎖表問題。此次問題成功解決也多虧了諮詢了DBA,仍是對數據庫不熟悉啊。數據庫