工作中redis锁的问题记录
2017-11-18 11:43
239 查看
受到公司刚大佬的影响,说了很多关于写总结的好处,于是我就试着写一写
。
上次上线的新车2,版本,有这么一个问题,我们服务端给app提供了一个上传图片的接口,图片是接受参数是一个数组,意思是等图片上传完之后,统一将图片id给我们就好了,但是因为上传接货单的上限是50张图片,所有如果一次就将图片全部上传并调用我们接口的话,app那边反应将会很慢,所以最后定的方案就是上传一张接货单就调用我们一次接口。
我们服务这边,是第一次上传借货单的时候就去创建一张接货单审核单,一个物流单对应一个物流单审核单,意味着一次上传的图片都挂在接货单审核单下面,因为app那边是上传接货单的时候是上传一次就掉一次接口,所以这里存在高并发的问题,第一次的代码是这个样的。
问题分析:
这个代码的锁的范围只是在创建接货单审核单的地方,而且而且创建借货单审核单,和保存图片是在同一个domain里面,所以是在同一个事务下面,当两个线程同时进来的时候,一个线程被挡住,一个进程进去创建了审核单,但是这个时候事务并没有提交,等第二个线程进来的时候,根本在数据库里面查不到记录,所以又创建了一遍,所以后台才会出现有两个审核单的情况。
问题解决:
最后我们是这样解决的,我们将锁的范围扩大,将创建借货单审核单和保存接货单信息放在同一个锁里面,而且是在同一个事务下面,这个的缺点是相应时间会长,但是能避免创建两个接货单审核单的情况。
代码如下:
。
上次上线的新车2,版本,有这么一个问题,我们服务端给app提供了一个上传图片的接口,图片是接受参数是一个数组,意思是等图片上传完之后,统一将图片id给我们就好了,但是因为上传接货单的上限是50张图片,所有如果一次就将图片全部上传并调用我们接口的话,app那边反应将会很慢,所以最后定的方案就是上传一张接货单就调用我们一次接口。
我们服务这边,是第一次上传借货单的时候就去创建一张接货单审核单,一个物流单对应一个物流单审核单,意味着一次上传的图片都挂在接货单审核单下面,因为app那边是上传接货单的时候是上传一次就掉一次接口,所以这里存在高并发的问题,第一次的代码是这个样的。
/** * 批量上传接货单 * * @param orderID * @param detailBos * @throws BusinessException */ @Override public void mutilCreateReceiveOrder(Integer orderID, List<FreshCarReceivingGoodsPhotoDetailBo> detailBos) throws BusinessException { if (orderID == null || detailBos == null){ throw new BusinessException("订单ID或者接货单不能为空"); } //创建一张接货单审核单 FreshCarReceivGoodsBo freshCarReceivGoodsBo = new FreshCarReceivGoodsBo(); freshCarReceivGoodsBo.setOrderID(orderID); Integer recID = this.create(freshCarReceivGoodsBo); //填充到接货单集合中 List<FreshCarReceivingGoodsPhotoDetailBo> list = new ArrayList<>(); for (FreshCarReceivingGoodsPhotoDetailBo bo: detailBos){ if (bo == null){ continue; } bo.setReceivGoodsID(recID); bo.setIsDelete(false); bo.setCreateTime(new Date()); list.add(bo); } freshCarReceivingGoodsPhotoDetailDomain.create(list); } /** * 创建收货审核单 * * @param freshCarReceivGoodsBo * @return * @throws BusinessException */ @Override public Integer create(FreshCarReceivGoodsBo freshCarReceivGoodsBo) throws BusinessException { if (freshCarReceivGoodsBo == null){ throw new BusinessException("收货审核单不能为空"); } FreshCarReceivGoods freshCarReceivGoods = BeanUtil.convert(freshCarReceivGoodsBo,FreshCarReceivGoods.class); Integer orderID = freshCarReceivGoods.getOrderID(); if (orderID == null ){ throw new BusinessException("订单ID不能为空"); } //查询该订单下是否存在收货审核单 RedisLock lock = null; try { lock = redisUtil.initLock(RedisKeyConstant.getKey(RedisKeyConstant.Business.COMMON, RedisKeyConstant.Project.COMMON, orderID.toString())); //获取锁 if(lock.lock(1000)){ FreshCarReceivGoodsBo bo = this.getByOrderID(orderID); if (bo != null){ if (bo.getCheckStatus().equals(ReceiveStatusEnums.ReceiveCheckStatusEnum.WAITCHECK) || bo.getCheckStatus().equals(ReceiveStatusEnums.ReceiveCheckStatusEnum.SAVE)) { return bo.getID(); } if (bo.getCheckStatus().equals(ReceiveStatusEnums.ReceiveCheckStatusEnum.PASS)){ throw new BusinessException("该订单已存在审核通过的接货审核单,无法再创建"); } } //初始状态为保存 freshCarReceivGoods.setCheckStatus(ReceiveStatusEnums.ReceiveCheckStatusEnum.SAVE.getValue()); freshCarReceivGoods.setCreateTime(new Date()); freshCarReceivGoods.setIsDelete(false); //保存 return freshCarReceivGoodsService.save(freshCarReceivGoods); }else{ throw new BusinessException("get lock fail"); } } finally { if (null != lock) { lock.unlock(); } }这样写的话,发现后面还是会一次上传还是会创建两张相同时间的接货单审核单。
问题分析:
这个代码的锁的范围只是在创建接货单审核单的地方,而且而且创建借货单审核单,和保存图片是在同一个domain里面,所以是在同一个事务下面,当两个线程同时进来的时候,一个线程被挡住,一个进程进去创建了审核单,但是这个时候事务并没有提交,等第二个线程进来的时候,根本在数据库里面查不到记录,所以又创建了一遍,所以后台才会出现有两个审核单的情况。
问题解决:
最后我们是这样解决的,我们将锁的范围扩大,将创建借货单审核单和保存接货单信息放在同一个锁里面,而且是在同一个事务下面,这个的缺点是相应时间会长,但是能避免创建两个接货单审核单的情况。
代码如下:
/** * 批量上传接货单 * * @param orderID * @param detailBos * @throws BusinessException */ @Override public void mutilCreateReceiveOrder(Integer orderID, List<FreshCarReceivingGoodsPhotoDetailBo> detailBos) throws BusinessException { if (orderID == null || detailBos == null){ throw new BusinessException("订单ID或者接货单不能为空"); } RedisLock lock = null; try { lock = redisUtil.initLock(RedisKeyConstant.getKey(RedisKeyConstant.Business.COMMON, RedisKeyConstant.Project.COMMON, "freshCar_order_"+orderID.toString())); if(lock.lock(2000)){ //创建一张接货单审核单 FreshCarReceivGoodsBo freshCarReceivGoodsBo = new FreshCarReceivGoodsBo(); freshCarReceivGoodsBo.setOrderID(orderID); Integer recID = this.create(freshCarReceivGoodsBo); //填充到接货单集合中 List<FreshCarReceivingGoodsPhotoDetailBo> list = new ArrayList<>(); for (FreshCarReceivingGoodsPhotoDetailBo bo: detailBos){ if (bo == null){ continue; } bo.setReceivGoodsID(recID); bo.setIsDelete(false); bo.setCreateTime(new Date()); list.add(bo); } freshCarReceivingGoodsPhotoDetailDomain.create(list); }else{ throw new BusinessException("get lock fail"); } } finally { if (null != lock) { lock.unlock(); } } }
相关文章推荐
- 数据库查询速度极慢【个人工作问题解决过程记录】
- 工作日常问题记录与解决 更新日期:2016/1/22
- 认真记录总结工作中的问题
- 记录这几天工作内容发现的兼容性问题
- 每日工作记录——W5500网口ping中出现的问题
- 好久没来,记录一下最近工作中遇到的问题
- 工作记录8:iOS 传值问题总结(7种传值完美介绍)
- 平时工作中遇到的问题点解决记录
- 关于屏幕适配问题的工作<个人记录>
- 工作中遇到的问题记录
- jedis 连接redis问题记录
- Redis 单点模式和集群模式代码测试及问题记录
- 将数据的初始化放到docker中的整个工作过程(问题记录)
- java redis client无法连接redis服务获取连接池问题处理记录
- redis存在大量脏页问题的追查记录
- 2014年11月5号工作中遇见的一些问题,记录一下.
- 前端工作面试问题(先记录,后面再一一解答)
- redis存在大量脏页问题的追查记录
- 记录下工作过程中,需要解决的一些问题
- 开始用博客记录工作中解决的问题。