文章目录
序
在15+2项目中由于使用redis分布式锁时加完锁忘记删锁而导致的一段故事,进而深入思考。
加锁不是一件好事,加锁时一定要慎之又慎,如果可以用逻辑处理并发影响或者依赖数据库处理那么就不要加锁,加锁对性能的影响极大,如果没有感受到影响,无他,并发量太小未达到瓶颈罢了,加锁的影响无论何时何地都是切实存在的。
判断是否加锁的唯一标准:并行时会不会造成数据错误。
个人历程,从最开始的不加锁,到加锁,再到滥用锁,再到精准用锁,最后到锁合并。
- 不加锁,最开始没有加锁,导致了二维码的重复使用,还导致了用户流水的加减错误(虽然也有可能是排序的问题,datetime的精度太低,但未加锁的原因最大)
- 使用锁,二维码限制住了,流水加减错误限制住了,(虽然自创的控制码的锁有各种各样的问题,优化了n个版本,也不知道现在这个怎么样,就是为了判断锁里面的值,减少一次mysql读取)
- 滥用锁,海报模块中由于加完锁之后忘记解锁,导致了一系列的问题,海报锁未删除–》人锁未删除–》mysql数据死锁(事务未提交,废话,一直有锁咋提交事务);加上所删除之后,不禁思考为什么会导致这么严重的后果,就是因为加的锁太多了,一段代码中一下加了三个锁,码锁、user锁、source锁、导致一个锁未删除,则产生连带效果,一发不可收拾。
- 精简锁,如果不是必须禁止用锁,也就说当并行时不会造成数据混乱或者逻辑混乱。
消费者领取海报时,一下加了三个锁,海报锁、user锁、source锁,其实仔细想想,user根本不需要加锁啊,海报的流水时单独计算的,并且不参与累计,那么为什么要对user加锁?user锁被滥用
消费者核销海报时,同样加了三个锁,海报锁、user锁、source锁,分析一下,海报锁已经由mysql计算了,加不加无所谓,user流水单独记录,更是不需要加!!user锁和海报锁被滥用。 - 锁合并,在思考锁时突然反应过来,红包回调时如果用户刚好在领取红包,那么其实会导致数据混乱的,也就说红包奖的控制和饮料奖的控制应该是两个控制,然后正准备再加上一个红包奖的锁,突然发现我锁的是人,也就是说以人为维度进行加锁,领取饮料、领取红包、回退红包、核销饮料、兑换饮料时,其实就都用的一个锁,这5件事只能存在一个,虽然锁的粒度大了,但是少了一个锁,并且也拆不开,因为额外奖励和定向奖励的存在,红包和饮料已经掺和在了一起,没办法分开,只能这样加锁,也算是歪打正着。
无必要不加锁,粒度越细越好,个数越少越好。
1. 老生常谈
以为优化好的锁:
rqrcodekey = JmlRedisKey.LOCK_QRCODE.concat(qrcode);
Boolean rqrcoderesB = redisUtils.redLock(rqrcodekey,JmlConstant.Properties.DOING,rkeyTime);
if(!rqrcoderesB) {
String rqrcoderes = (String) redisUtils.get(rqrcodekey);
logger.info("箱码发生码同步1:{}",rqrcoderes);
if (rqrcoderes == null || StrUtil.isBlank(rqrcoderes) || rqrcoderes.equals(JmlConstant.Properties.FAILED)) {
redisUtils.set(rqrcodekey, JmlConstant.Properties.DOING, rkeyTime);
} else if (rqrcoderes.equals(JmlConstant.Properties.SUCCESS)) {
return new ResponseBean<>("401", "该码已使用过", "该码已使用过");
} else if (rqrcoderes.equals(JmlConstant.Properties.DOING)) {
while (rqrcoderes.equals(JmlConstant.Properties.DOING)) {
Thread.sleep(sleepTime);
rqrcoderes = (String) redisUtils.get(rqrcodekey);
logger.info("箱码发生码同步2:{}",rqrcoderes);
if (rqrcoderes == null || StrUtil.isBlank(rqrcoderes) || rqrcoderes.equals(JmlConstant.Properties.FAILED)) {
redisUtils.set(rqrcodekey, JmlConstant.Properties.DOING, rkeyTime);
break;
}else if(rqrcoderes.