Redis分布式锁

并发场景下,多个进程或线程共享资源的读写,需要保证对资源的访问互斥。

在单机系统中,我们可以使用Java并发包中的API、synchronized关键字等方式。

在分布式系统下,这些方式不再适用,因为不同的节点的Java后端代码都是单独运行的,所以不是需要单节点的锁,是需要多节点的锁,即:增加第三方唯一组件,实现分布式锁。

常见的分布式锁的实现方案有:基于数据库、基于Redis、基于Zookeeper。

分布式锁理论

1、命令处理阶段:Redis使用单线程处理,同一个key同时只有一个线程能够处理,没有多线程竞争问题。

2、SET key value NX PX milliseconds 命令在不存在key的情况下添加具有过期时间的key,为安全加锁提供支持。

3、Lua脚本和DEL命令为安全解锁提供可靠支撑。

于是我们有了以下的redis分布式锁代码

 /**
     * 尝试获取分布式锁
     * @param jedis redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识//不一定要是请求id,只要是能标识出是谁加锁的,方便后面释放锁
     * @param expireTime 过期时间
     * @return
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
        return "OK".equals(result);
    }
    
       /**
     *释放锁
     * @param jedis
     * @param lockKey 锁
     * @param requestId 请求标识//不一定要是请求id,只要是能标识出是谁加锁的,方便后面释放锁
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId){
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

这样的代码,能够实现:

1、使用set命令NX、PX选项进行加锁,保证了加锁互斥,避免了死锁;

2、使用lua脚本解锁,防止解除其他线程的锁;

3、加锁、解锁命令都是原子操作;

但前提条件:单机版Redis、开启AOF持久化方式并设置appendfsync=always

在哨兵模式和集群模式下存在问题,原因如下:

1、哨兵模式和集群模式基于主从架构,主从之间通过命令传播实现数据同步,而命令传播是异步的。

2、就存在主节点数据写入成功,在还未通知从节点情况下,主节点就宕机的可能。

3、当从节点通过故障转移提升为新的主节点后,其他线程就有机会重新加锁成功,导致不满足分布式锁的互斥条件。

集群下的分布式锁

由于主从同步基于异步复制原理,所以哨兵模式、集群模式天生无法满足此条件。

解决方案是:RedLock(Redis Distribute Lock),有多种实现,最后推荐的是 Redisson

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version> <!-- 请使用最新版本 -->
</dependency>

特点:可重入特性,按照RedLock进行实现,支持独立实例模式、集群模式、主从模式、哨兵模式。

有的机制:加锁机制、锁互斥机制、Watch dog 机制、可重入加锁机制、锁释放机制对 Redisson 实现分布式锁的底层原理进行分析。

Redisson 优点

1、通过 Watch Dog 机制很好的解决了锁的续期(监控续签,防止锁失效)问题。

2、和 Zookeeper 相比较,Redisson 基于 Redis 性能更高,适合对性能要求高的场景。

3、通过 Redisson 实现分布式可重入锁,比原生的 SET mylock userId NX PX milliseconds + lua 实现的效果更好些,虽然基本原理都一样,但是它帮我们屏蔽了内部的执行细节。

4、在等待申请锁资源的进程等待申请锁的实现上也做了一些优化,减少了无效的锁申请,提升了资源的利用率。