最近项目上出现了很奇怪的一个问题,经过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,仍是对数据库不熟悉啊。数据库