记一次数据库死锁追踪过程
来源:互联网 发布:量化和编程 编辑:程序博客网 时间:2024/06/04 17:58
记一次数据库死锁追踪过程
案例背景:从网上爬了一批数据,把用户信息录入mysql数据库中了,但是后面爬取到的信息需要根据用户名和id去更新。数据量100w+,要修改的数据量30w+.
业务逻辑代码描述
1.访问数据库获取id,name,放入map中,key=name,value=id
2.访问xxx.txt获取用户名,粉丝数量,关注数量放入list中,起事务
3.循环list,在list中循环访问数据库根据id获取记录数,然后更新该记录
4.结束事务。
前奏:
总结:这种方案在数据量小的时候运行还可以,现在有100w+数据量,同时在for循环中访问数据库,导致的结果就是运行时内存不断增加,循环后期fgc从每30秒一次到每秒一次,即使是显示的设置已处理过的entity对象为null,也解决不了问题,同时将map中的数据清除也不行。
改进:
由于我的数据库连接是自己开发的,持久化层也是自己开发的,所以就加了一个直接拼接update sql来解决返回对象过大的问题。将步骤三改为:
循环list,在list中循环生成kv对象,k是columnName,v是值,然后把id一起传入daoclient(自己开发的简单dao组件)方法中。
Sql: update user set kname=vname,kfans=vfans,kfocus=vfocus where id =vid;
为了使用Java8的特性,将list通过stream并行的方式去处理每条记录,但是这出现了另一个问题,数据库链接最大设置的5不够用了,底层使用的list存储每个链接,经过改进使用LinkedBlockingDeque存储链接就不会出现不够用的情况了,由于是并行访问数据库,所以链接不够用的情况还可能会存在。
高潮:
根据上面描述的过程可能大牛们和菜鸟们都知道一些原因和原理,那现在为啥出现死锁呢,异常如下:
java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734) at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:995) at com.coderman.daoclient.connections.StatementTask.realExeUpdateSql(StatementTask.java:446) at com.coderman.daoclient.connections.StatementTask.exeUpdate(StatementTask.java:389) at com.coderman.daoclient.dao.impl.IDaoImpl.updateEntity(IDaoImpl.java:56) at com.coderman.daotest.dao.IDaoImplTest.lambda$main$0(IDaoImplTest.java:59) at com.coderman.daotest.dao.IDaoImplTest$$Lambda$1/451111351.accept(Unknown Source) at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291) at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1689) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
一开始以为是自己的daoclient组件的问题,但是根据异常代码提示显示应该不是dao的问题。
开始百度:
1.找到MySQL关于死锁的命令一一重试:
show full processlist;发现3个update语句在等待执行。
没有发现问题,经过其他提示,发现只要有不同sql或者不同的程序访同问一张表就可能出现死锁。于是使用下面的命令:
SELECT * FROM information_schema.innodb_trx
查出确实是上面三个update语句的问题,那为什么update语句会造成死锁呢,这里大概都知道原因了吧。
总结:因为xxx.txt文件里有重复的数据那三条update语句本来应该执行了,但是后面有出现相同的update语句,针对的是同一行,使用了事务,之前提交的update语句还没有完成,后面又有相同行的update请求,所以导致死锁了。
在循环中出现死锁之后,其他行的update就变慢了,性能急速下降。。。
尾声:
解决方案:
1.手动将重复的三条记录删除
2.在程序中使用set集合过滤
3.在程序中判断重复则忽略。
- 记一次数据库死锁追踪过程
- 一次死锁追踪经历
- 一次死锁追踪经历
- 一次死锁追踪经历
- 一次死锁追踪经历
- 记一次线上数据库死锁定位
- 记一次bug追踪
- 一次追踪盗QQ木马的过程
- 一次mysql死锁的排查过程
- 记录一次 Mysql 死锁排查过程
- 一次mysql死锁的排查过程
- 一次Mysql死锁排查过程的全纪录
- 记一次Native层崩溃追踪
- 第一次遇到死锁——记一次程序卡住问题的错误排查过程
- mutex死锁追踪
- 关于SQL server2005+SP3的死锁的一次诊断过程
- 记一次服务器宕机后数据库恢复的过程
- 查看数据库死锁情况的存储过程
- c语言的左旋
- Hexo 发布文章到git.io步骤
- Linux中的FTP服务
- java.lang.reflect.InvocationTargetException异常处理
- 《linux内核设计与实现-笔记1》
- 记一次数据库死锁追踪过程
- ES6 let const
- C语言的static--理解
- http2相关协议详解(express中开启http2流程)
- 实验七:将menu设计为可重用的子系统
- Some Scripts
- MRPT地址
- Lua 协同程序(coroutine)
- Linux笔记记录5 写时拷贝