写点什么

简单实用的 redis 分布式锁

作者:Rubble
  • 2022 年 4 月 15 日
  • 本文字数:1529 字

    阅读完需:约 5 分钟

基于 setnx 加锁,基于 lua 实现锁释放。

实现了根据 key 及唯一标识 requestId 进行加锁,并设置了超时时间;释放锁时根据 key 及 requestId 进行释放。


SET_IF_ABSENT 没有 key 才设置,expire 设置超时时间,作为原子性操作


RedisCallback<Boolean> callback = (connection) -> {    return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);};return (Boolean)redisTemplate.execute(callback)
复制代码


直接上代码,可以直接用的工具类


@Slf4jpublic class RedisDistributedLock {   private RedisTemplate redisTemplate;   public RedisDistributedLock(RedisTemplate redisTemplate){    this.redisTemplate = redisTemplate;  }   public static final String UNLOCK_LUA;   /**   * 释放锁脚本,原子操作   */  static {    StringBuilder sb = new StringBuilder();    sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");    sb.append("then ");    sb.append("    return redis.call(\"del\",KEYS[1]) ");    sb.append("else ");    sb.append("    return 0 ");    sb.append("end ");    UNLOCK_LUA = sb.toString();  }    /**   * 获取分布式锁,原子操作   * @param lockKey   * @param requestId 唯一ID, 可以使用UUID.randomUUID().toString();   * @param expire   * @param timeUnit   * @return   */  public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {    try{      RedisCallback<Boolean> callback = (connection) -> {        return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);      };      return (Boolean)redisTemplate.execute(callback);    } catch (Exception e) {      log.error("redis lock error.", e);    }    return false;  }   /**   * 释放锁   * @param lockKey   * @param requestId 唯一ID   * @return   */  public boolean releaseLock(String lockKey, String requestId) {    RedisCallback<Boolean> callback = (connection) -> {      return connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN ,1, lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")));    };    return (Boolean)redisTemplate.execute(callback);  }   /**   * 获取Redis锁的value值   * @param lockKey   * @return   */  public String get(String lockKey) {    try {      RedisCallback<String> callback = (connection) -> {        return new String(connection.get(lockKey.getBytes()), Charset.forName("UTF-8"));      };      return (String)redisTemplate.execute(callback);    } catch (Exception e) {      log.error("get redis occurred an exception", e);    }    return null;  } }
复制代码


此工具简易轻量,基本满足加锁的一些场景。

VS redission 分布式锁

redisson 实现了加锁尝试时间


看门狗延长锁持有时间。


redission 支持多样锁如可重入锁、读写锁、信号量等。


直接用 setNx 的问题,无超时时间程序意外挂掉,导致 key 不释放,好像新版本有 setnx+过期时间的方法。


释放锁直接用 delete 可能删除非当前线程的锁,增加唯一 value 校验,两步操作非原子性。

用户头像

Rubble

关注

还未添加个人签名 2021.06.01 加入

还未添加个人简介

评论

发布
暂无评论
简单实用的redis分布式锁_4月日更_Rubble_InfoQ写作平台