欢迎使用CSDN-markdown编辑器
来源:互联网 发布:编程的那些事儿(10 编辑:程序博客网 时间:2024/06/05 18:54
论mysql死锁的切入点以及解决方法
>本文主要讲java spring 多线程造成的死锁问题
- 死锁举例*
- 死锁快速解决*
- 死锁本质解决*
①
@Override@Transactional(rollbackFor=Exception.class)public GeneralReponse saveOrder(OpOrder opOrder, GeneralRequest request) { Long expireTime = opOrder.getCreationTime().getTime()/1000 + PayConstant.OVER_TIME*60; //加占用库存 increaseOccupy(opOrder); //存入op_order表 opOrderMapper.insertSelective(opOrder); //存入op_order_log表 opOrderLogMapper.insertSelective(new OpOrderLog(opOrder.getUserId(), opOrder.getOrderCode(), opOrder.getActivityId(), opOrder.getSaledId(), expireTime, opOrder.getCreationTime(),opOrder.getNum())); Map<String,Object> resp = new HashMap<String,Object>(1, 1.0f); resp.put("orderCode", opOrder.getOrderCode()); return new GeneralReponse(CodeConstant.SUCCESS, null,resp);}//加占用库存操作private void increaseOccupy(OpOrder opOrder) { OpActivitySetTime opActivitySetTime = opActivitySetTimeMapper.selectByPrimaryKey(opOrder.getTimeId()); if(opActivitySetTime == null) { throw new ParamErrorException(CodeConstant.PARAM_ERROR, "timeId is error"); } Long stock = opActivitySetTime.getStock(); Map<String,Object> paramData = new HashMap<String, Object>(4, 1.0f); paramData.put("activityId", opOrder.getActivityId()); paramData.put("setId", opOrder.getSetId()); paramData.put("timeId", opOrder.getTimeId()); paramData.put("dateTime", opOrder.getDateTime()); OpOrderSaled opOrderSaled = opOrderSaledMapper.queryOrderSaleInfo(paramData); Map<String,Object> map = new HashMap<String, Object>(2, 1.0f); if(opOrderSaled == null) { opOrderSaled = new OpOrderSaled(opOrder.getActivityId(), opOrder.getSetId(), opOrder.getTimeId(), 1L, opOrder.getDateTime()); if(opOrderSaledMapper.insertSelective(opOrderSaled) != 1) { opOrderSaled = opOrderSaledMapper.queryOrderSaleInfo(paramData); map.put("id", opOrderSaled.getId()); map.put("stock", stock); map.put("num", opOrder.getNum()); if(opOrderSaledMapper.increaseOccupyById(map) != 1) { throw new BusinessErrorException(CodeConstant.BUSINESS_ERROR, "您选择的体验名额已用光,请选择其他的时间或体验"); } } } else { map.put("id", opOrderSaled.getId()); map.put("stock", stock); map.put("num", opOrder.getNum()); if(opOrderSaledMapper.increaseOccupyById(map) != 1) { throw new BusinessErrorException(CodeConstant.BUSINESS_ERROR, "您选择的体验名额已用光,请选择其他的时间或体验"); } } opOrder.setSaledId(opOrderSaled.getId());}
②
@Transactional(rollbackFor=Exception.class)public boolean cancelProcedure(String orderCode,Long uid,Integer cancalType,Integer payType,Long saledId,Integer num) { Map<String,Object> paramData = new HashMap<String, Object>(); paramData.put("orderCode", orderCode); int flag = 0; Date now = new Date(); try{ //删除op_order_log表的记录 int deleteNum = opOrderLogMapper.deleteByOrderCode(orderCode); if(deleteNum == 1) { paramData.put("updateTime", now); paramData.put("status", 6); //更改订单状态 opOrderMapper.updateByOrderCode(paramData); Map<String,Object> map = new HashMap<String, Object>(2, 1.0f); map.put("id", saledId); map.put("amount", num); opOrderSaledMapper.decreaseOccupyById(map); flag = 1; } } catch (Exception e) { logger.error("return activity stock occured error", e); throw new RuntimeException("server error"); } Map<String,String> cacheKey = new HashMap<String,String>(1,1.0f); cacheKey.put(String.valueOf(uid)+RedisKeyInfo.USER_ORDER.key(), orderCode); try { batchRemoveOrderCache(cacheKey); } catch(RuntimeException e) { logger.error("batch remove cache occured error", e); } if(flag == 0) { return false; } return true;}
①是下单的操作 ,先在占用库存表做增加操作,然后再订单表以及订单中间表进行插入操作
②是定时取消订单的操作,先删除订单中间表的超时订单,然后更新订单表的状态,回补库存
以上coding有几种导致死锁可能
1:未加索引
因这个项目用的是mysql数据库,innodb引擎。对where判断字段没有加索引,导致在做删除订单中间表操作的时候,使用的是表锁,对整张订单中间表进行了锁定,
②中的 opOrderLogMapper.deleteByOrderCode(orderCode)占据了这把锁,然后进行更新订单表状态的操作,
①中的opOrderMapper.insertSelective(opOrder)做完插入操作占据了订单表的表锁,紧接着做插入订单表的操作
此时①要②中的订单表的表锁,②需要①订单中间表的表锁,造成了死锁
2:两个线程都引入事务 同时对两张表进行增删改操作 如果增删改操作相同的数据行就会出现死锁
如:
①中的对占用库存表进行增加库存的操作opOrderSaledMapper.increaseOccupyById(map),
②中的对占用库存表进行了减的操作opOrderSaledMapper.decreaseOccupyById(map);
如果操作的相同数据行,都要等待双方事务的结束才能进行,也会造成死锁
那如何解决呢
从上面可以看出,死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。
1:暴力解决,直接在mysql杀死进程id
①.查看下在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
②.杀死进程id(就是上面命令的trx_mysql_thread_id列)
kill 线程ID
2:本质解决
这个问题 很难避免的 有时只能从业务上规避 或者降低事务的隔离级别,我们在coding的过程中,需尽量保证事务不会相互占用,执行顺序尽量保持一致,保证操作的原子性
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 启动dubbo报错解决:java.lang.RuntimeException: java.io.IOException: invalid constant type: 15
- HDU6069-Counting Divisors 约数个数定理+素数分解
- 二元多项式(简单链表操作,注释很好理解)
- 常见的多媒体知识解析
- 字符串练习——用不同的方法实现字符串平移问题
- 欢迎使用CSDN-markdown编辑器
- 【NOIP2017模拟A组模拟8.5】带权排序
- 求凸包 cogs896
- Lintcode各位相加
- Java哈希表及其应用
- Android Studio 应用程序管理功能思路与代码
- 【CUGBACM15级BC第14场 B】Harry And Dig Machine
- 【hautoj 1282】ykc想吃好吃的 【循环最大字段和】
- P2161 [SHOI2009]Booking 会场预约