Redis实现分布式锁

来源:互联网 发布:华三交换机端口镜像 编辑:程序博客网 时间:2024/06/11 03:47

Redis实现分布式锁

分布式锁的一些问题

  1. 并发问题,若多个客户端同时上锁,结果只允许一个客户端成功,其他失败,可以利用redis的SETNX 命令来实现,该命令允许若给定的 key 已经存在,则 SETNX 不做任何动作,设置成功,返回 1 ,设置失败,返回 0 。
  2. 上锁后解锁的问题,可以考虑使用redis key的ttl过期,通过PEXPIRE来设置key的自动过期。若不使用自动过期特性,则需要在锁定结束后使用DEL命令来进行解锁,这种情况有可能发生某个客户端已经crash,于是这个锁会永远锁定,导致其他客户端无法获得锁。

Redis2.6以后新特性

redis2.6.12以后 SET 命令的行为可以通过一系列参数来修改
1. EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value
1. PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value
1. NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value
1. XX :只在键已经存在时,才对键进行设置操作。

使用新特性来解决分布式锁

  1. 使用命令 SET key value NX EX second 设置锁的同时为锁设置TTL过期时间,若当锁已经存在时返回nil。

Redisson解决方法

Redisson是redis的一个java客户端,实现了分布式锁和同步器,Redisson的分布式锁RLock Java对象实现了java.util.concurrent.locks.Lock接口,同时还支持自动过期解锁。

RLock lock = redisson.getLock("anyLock");// 最常见的使用方法lock.lock();// 支持过期解锁功能// 10秒钟以后自动解锁// 无需调用unlock方法手动解锁lock.lock(10, TimeUnit.SECONDS);// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);...lock.unlock();

以上是redisson使用锁的方法,它的原理采用了lua脚本的方式,这样能兼容redis 2.6.12之前的版本,锁的核心源码如下RedissonLock.java

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {        internalLockLeaseTime = unit.toMillis(leaseTime);        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,        //先检查key是否存在                  "if (redis.call('exists', KEYS[1]) == 0) then " +        //key不存在,创建Hash                      "redis.call('hset', KEYS[1], ARGV[2], 1); " +        //设置过期时间                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +                      "return nil; " +                  "end; " +        //若锁已经存在,并且是同样获得锁的线程调用                  "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +        //复用锁,为锁+1                      "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +        //延长锁定时间                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +                      "return nil; " +                  "end; " +         //若无法获得锁,返回锁的剩余时间                  "return redis.call('pttl', KEYS[1]);",                    Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));    }
原创粉丝点击