redis分布式锁的8大坑总结梳理

目录
  • 前言
  • 1 非原子操作
  • 2 忘了释放锁
  • 3 释放了别人的锁
  • 4 大量失败请求
  • 5 锁重入问题
  • 6 锁竞争问题
    • 6.1 读写锁
    • 6.2 锁分段
  • 7 锁超时问题
    • 8 主从复制的问题

      前言

      在分布式系统中,由于redis分布式锁相对于更简单和高效,成为了分布式锁的首先,被我们用到了很多实际业务场景当中。

      但不是说用了redis分布式锁,就可以高枕无忧了,如果没有用好或者用对,也会引来一些意想不到的问题。

      今天我们就一起聊聊redis分布式锁的一些坑,给有需要的朋友一个参考。

      redis分布式锁的8大坑总结梳理

      1 非原子操作

      使用redis的分布式锁,我们首先想到的可能是setNx命令。

      if(jedis.setnx编程客栈(lockKey,val)==1){
      jedis.expire(lockKey,timeout);
      }
      

      容易,三下五除二,我们就可以把代码写好。

      这段代码确实可以加锁成功,但你有没有发现什么问题?

      加锁操作和后面的设置超时时间是分开的,并非原子操作

      假如加锁成功,但是设置超时时间失败了,该lockKey就变成永不失效。假如在高并发场景中,有大量的lockKey加锁成功了,但不会失效,有可能直接导致redis内存空间不足。

      那么,有没有保证原子性的加锁命令呢?

      答案是:有,请看下面。

      2 忘了释放锁

      上面说到使用setNx命令加锁操作和设置超时时间是分开的,并非原子操作。

      而在redis中还有set命令,该命令可以指定多个参数。

      Stringresult=jedis.set(lockKey,requestId,"NX","PX",expireTime);
      if("OK".equals(result)){
      returntrue;
      }
      returnfalse;

      其中:

      • lockKey:锁的标识
      • requestId:请求id
      • NX:只在键不存在时,才对键进行设置操作。
      • PX:设置键的过期时间为 millisecond 毫秒。
      • expireTime:过期时间

      set命令是原子操作,加锁和设置超时时间,一个命令就能轻松搞定。

      使用set命令加锁,表面上看起来没有问题。但如果仔细想想,加锁之后,每次都要达到了超时时间才释放锁,会不会有点不合理?加锁后,如果不及时释放锁,会有很多问题。

      分布式锁更合理的用法是:

      • 手动加锁
      • 业务操作
      • 手动释放锁
      • 如果手动释放锁失败了,则达到超时时间,redis会自动释放锁。

      大致流程图如下:

      redis分布式锁的8大坑总结梳理

      那么问题来了,如何释放锁呢?

      伪代码如下:

      try{
      Stringresult=jedis.set(lockKey,requestId,"NX","PX",expireTime);
      if("OK".equals(result)){
      returntrue;
      }
      returnfalse;
      }finally{
      unlock(lockKey);
      }

      需要捕获业务代码的异常,然后在finally中释放锁。换句话说就是:无论代码执行成功或失败了,都需要释放锁。

      编程客栈时,有些朋友可能会问:假如刚好在释放锁的时候,系统被重启了,或者网络断线了,或者机房断点了,不也会导致释放锁失败?

      这是一个好问题,因为这种小概率问题确实存在。

      但还记得前面我们给锁设置过超时时间吗?即使出现异常情况造成释放锁失败,但到了我们设定的超时时间,锁还是会被redis自动释放。

      但只在finally中释放锁,就够了吗?

      3 释放了别人的锁

      做人要厚道,先回答上面的问题:只在finally中释放锁,当然是不够的,因为释放锁的姿势,还是不对。

      哪里不对?

      答:在多线程场景中,可能会出现释放了别人的锁的情况。

      有些朋友可能会反驳:假设在多线程场景中,线程A获取到了锁,但如果线程A没有释放锁,此时,线程B是获取不到锁的,何来释放了别人锁之说?

      redis分布式锁的8大坑总结梳理

      扫一扫手机访问