Redis实现分布式锁

2022-08-26 19:58:45 浏览数 (1)

Redis被经常用来实现分布式锁,本文主要讲述redis如何实现一个分布式锁的demo。

获取锁

redis分布式锁的实现主要是通过redis的set命令实现:

代码语言:javascript复制
SET key value nx PX milliseconds
  • nx : 当key存在的时候才将set对应的key,value写入到redis数据库里面。如果设置成功之后,就返回OK,已经存在就返回nil,当前set命令也是一个原子操作,可以作为分布式锁。
  • px milliseconds: 为当前key设置过期时间,当时间过了之后会将当前key删除。主要是方式获取锁之后未正常释放,导致死锁,一直无法获取锁。超时时间需要合理设置,需要保证当前业务能够处理完。
代码语言:javascript复制
private static final String LOCK_SUCCESS = "OK";

  private static final Long UNLOCK_SUCCESS = 1L;

  //
  private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

  private final JedisCluster cluster;

  private final long lockTimeout;

  private final String lockKey;

  public RedisDistributedLockImpl(JedisCluster cluster, String lockKey, long lockTimeout) {

    this.cluster = cluster;
    this.lockTimeout = lockTimeout;
    this.lockKey = lockKey;
  }

  @Override
  public String lock() {
    long end = System.currentTimeMillis()   lockTimeout;
    String requireToken = String.valueOf(System.currentTimeMillis());
    int expireTime = 300 * 1000;
    SetParams params = new SetParams().nx().px(expireTime);
    while (System.currentTimeMillis() < end) {
      try {
        String result = cluster.set(lockKey, requireToken, params);
        if (LOCK_SUCCESS.equals(result)) {
          System.out.println("lock success");
          return requireToken;
        }
        System.out.println("lock failed trying");
        Thread.sleep(100);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }
    }
    return requireToken;
  }

释放锁

释放锁,主要是需要通过原子操作将锁对应的key删除,可以通过下面lua脚本删除。

代码语言:javascript复制
if redis.call('get', KEYS[1]) == ARGV[1] then 
   return redis.call('del', KEYS[1]) 
else 
    return 0 
end

释放锁的详细代码:

代码语言:javascript复制
@Override
  public boolean unlock(String identify) {
    if (identify == null || identify.trim().length() == 0) {
      return false;
    }
    Object obj = new Object();
    try {
      obj = cluster.eval(UNLOCK_SCRIPT, Collections.singletonList(lockKey), Collections.singletonList(identify));
      if (UNLOCK_SUCCESS.equals(obj)) {
        System.out.println("release lock success, requestToken:"   identify);
        return true;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.out.println("release lock failed, requestToken:"   identify   ", result:"   obj);
    return false;
  }

测试分布式锁

使用下面代码模拟秒杀场景:

代码语言:javascript复制
public class RedisDistributedLockTest {

  static int n = 500;

  public static void secKill() {
    System.out.println("商品数量:"   --n);
  }

  public static void main(String[] args) {
    Runnable runnable = () -> {
      RedisDistributedLockImpl lock = null;
      String unLockIdentify = null;
      try {
        Set<HostAndPort> set = new HashSet<>();
        set.add(new HostAndPort("127.0.0.1", 30001));
        JedisPoolConfig config = new JedisPoolConfig();
        JedisCluster cluster = new JedisCluster(set, config);
        lock = new RedisDistributedLockImpl(cluster, "test1", 20000);
        unLockIdentify = lock.lock();
        System.out.println(Thread.currentThread().getName()   "正在运行");
        secKill();
      } finally {
        if (lock != null) {
          lock.unlock(unLockIdentify);
        }
      }
    };
    System.out.println("------------------开始--------------");
    for (int i = 0; i < 100; i  ) {
      Thread t = new Thread(runnable);
      t.start();
    }
  }

}

执行结果如下:

0 人点赞