基于redis的分布式锁(原子性操作)

分布式锁的实现方式

分布式锁的实现方式一般分为3种:数据库乐观锁、基于redis的分布式锁、基于zookeeper的分布式锁
为了确保分布式锁可用,至少要保证所得实现满足4种条件:
1.互斥性。在任意时刻,只有一个客户端能持有锁。
2.不会发生死锁。即使有一个客户端在持有锁期间挂掉没有主动释放锁,也要保证后续其他客户端可以加锁。
3.具有容错性。只要大部分的redis节点正常运行,客户端就可以加锁和解锁。
4.加锁和解锁必须保证是同一个客户端,不能把别人的锁解除了

基于redis的分布式锁

加锁

参考网上例子,写出以下代码
在这里插入图片描述

redisService.set(key, value, “nx”, “ex”, time) 这个是加锁的代码
key 为锁的唯一标识。
value 为验证客户端唯一性的参数,可以使用 UUID.randomUUID().toString() 获取。
nx 为SET IF NOT EXIST,key 存在时不做任何操作。
ex 为设置过期时间,具体时间由第五个参数决定。
time 为过期时间。

解锁

在这里插入图片描述

用上面生成的 value 和redis里面获取的 value 进行比较,相同则表示是相同的客户端,之后释放锁即可。
这里的问题是分为两步操作,不是原子性的,当第一步执行完成后锁可能因为过期销毁,就可能会发生另一个客户端刚拿到锁就被释放的问题。

最好的解决方式是通过 redis 的 eval() 方法,这个方法可以执行一段脚本,脚本运行是原子性的,在脚本运行期间没有客户端可以操作,下面为拷贝过来的代码

public class RedisTool {

    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    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;

    }

}

关于 eval() 方法的参数,大致有四个参数
1.script :一段需要执行的脚本
2.numkeys: 用于指定键名参数的个数
3.key [key …]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
4.arg [arg …]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值