由index_merge引发的死锁事件
来源:互联网 发布:阿里云tts 编辑:程序博客网 时间:2024/05/18 03:51
问题:最近出现大量如下报错
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) at com.mysql.jdbc.Util.getInstance(Util.java:386) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1065) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4074) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4006) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2719) at com.mysql.jdbc.PreparedStatement.executeInternal(Prepa
有没有发现每次mysql报的所有死锁都是类似这个的问题,其实我们根据他只能判断出是哪个sql出问题了,但是并不知道为啥出问题了,那么就看看 出问题的sql:
update f_n_order_info set last_updated = now(),root_id = id where order_no = #{orderNo} and last_updated = #{lastUpdated}
sql特征:
order_no:唯一索引
last_updated:普通索引
初判:
以为逻辑是事务范围太大,所以决定缩小事务范围
推翻:
既然事务范围太大,逻辑中还有insert,update,query为啥只有这这个update语句总是死锁,说不通
再判:
sql语句中where后面有两个索引做为条件,于是找到度娘
不恰当的update语句使用主键和索引导致mysql死锁
以为是update语句中where条件用了主键索引和主键索引,行级锁并不是直接锁记录,而是锁索引,如果一条SQL语句用到了主键索引,mysql会锁住主键索引;如果一条语句操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引,但是我们的sql中存在只有主键索引的update语句,如果先执行了这种update语句就会使另一个sql语句无法获取到锁。
以及接近事实,但是还不是事实,原因是我们的实际场景中并没有出现博客中说的,所以又pass了,下面就要揭开答案的真相了。
终判:
从mysql的日志来看:
看到加重地方的提示事务2没有获取到gap锁,其实我不明白什么是间隙锁,先普及一下:
如下图所示,事务A在第一次查询时得到1条记录,在第二次执行相同查询时却得到两条记录。从事务A角度上看是见鬼了!
这就是幻读,RC级别下尽管加了行锁,但还是避免不了幻读。
innodb的RR隔离级别可以避免幻读发生,怎么实现?当然需要借助于锁了!
为了解决幻读问题,innodb引入了gap锁。
在事务A执行:
update msg set message=‘订单’ where token=‘asd’;
innodb首先会和RC级别一样,给索引上的记录添加上X锁,此外,还在非唯一索引’asd’与相邻两个索引的区间加上锁。
这样,当事务B在执行
insert into msg values (null,‘asd',’hello’); commit;
时,会首先检查这个区间是否被锁上,如果被锁上,则不能立即执行,需要等待该gap锁被释放。这样就能避免幻读问题。
这个图是其实是说事务1和事务2中的两个索引互相持有锁
解决方案:
1、关掉mysql的index_merge
2、 update f_n_order_info
set last_updated = now(),root_id = id
where order_no = #{orderNo}
and last_updated = #{lastUpdated}
将order_no改为id,因为id不存在gap锁
- 由index_merge引发的死锁事件
- index_merge引发的死锁排查
- index_merge引发的死锁排查
- index_merge引发的死锁排查
- index_merge引发的死锁排查
- 关于数据库index_merge引发的死锁排查
- 一个由阻塞队列引发的类死锁案例
- 由一次上课睡觉事件引发的联想
- 由UIImageView中的UIButton不响应事件引发的
- 由UIImageView中的UIButton不响应事件引发的
- 由按钮和图片引发的事件传递血案
- 由Monkey测试引发的跨多个进程的Android系统死锁问题分析
- 由fastlock引发的...
- Socket引发的死锁问题。
- Bitmap index引发的死锁
- 由Typedef引发的问题
- 由InvocationTargetException引发的思考
- 由UseSubmitBehavior引发的问题
- 阿里云批量计算心得
- android 更新api current.txt
- BZOJ 3252: 攻略 贪心 树链剖分
- 服务器安全管理总结
- 7.3霍夫变换
- 由index_merge引发的死锁事件
- IE7下设置overflow-y: scroll出现滚动条的问题解决办法
- Map自定义键时注意事项
- java避开基本数据类型转换列表陷阱
- 注册邮箱验证激活技术
- ORM框架学习总结
- 大数据常见问题数据倾斜
- 主元素
- Welcome to GBaseDBer’s blog