IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Redis源码解析:如何实现分布式锁? -> 正文阅读

[大数据]Redis源码解析:如何实现分布式锁?

在这里插入图片描述

前言

对分布式锁不太了解的小伙伴,可以先看一下这篇文章
https://mp.weixin.qq.com/s/8fdBKAyHZrfHmSajXT_dnA

Redis分布式锁加锁

最开始的分布式锁是使用setnx+expire命令来实现的。setnx设置成功返回1,表示获取到锁,返回0,表示没有获取到锁,同时为了避免显示释放锁失败,导致资源永远也不释放,获取到锁后还会用expire命令设置锁超时的时间。

但有个问题就是setnx+expire不是原子性的,有可能获取到锁后,还没执行expire命令,也没执行释放锁的操作,服务就挂了,这样这个资源就永远也不会访问到了。

为了解决这个问题,Redis 2.6.12版本以后,为set命令增加了一系列的参数,我们此时用NX和PX参数就可以解决这个问题。

所以现在Redis分布式锁的加锁命令如下

SET resource_name random_value NX PX 30000

NX只会在key不存在的时候给key赋值,PX通知Redis保存这个key 30000ms,当资源被锁定超过这个时间时,锁将自动释放

random_value最好是全局唯一的值,保证释放锁的安全性

# 设置成功返回OK
127.0.0.1:6379> SET lock1 100 NX PX 30000
OK
127.0.0.1:6379> SET lock1 100 NX PX 30000
(nil)

当某个key不存在时才能设置成功。这就可以让多个并发线程同时去设置同一个key,只有一个能设置成功。而其他线程设置失败,也就是获得锁失败

Redis分布式锁解锁

解锁不能简单的使用如下命令

del resource_name 

因为有可能节点A加锁后执行超时,锁被释放了。节点B又重新加锁,A正常执行到del命令的话就把节点B的锁给释放了。所以在解锁之前先判断一下是不是自己加的锁,是自己加的锁再释放,不是就不释放。所以伪代码如下

if (random_value.equals(redisClient.get(resource_name))) {
    del(key)
}

因为判断和解锁是2个独立的操作,不具有原子性,还是有可能会出问题。所以解锁的过程要执行如下的Lua脚本
,通过Lua脚本来保证判断和解锁具有原子性。

if redis.call("get", KEYS[1]) == ARGV[1] then 
    return redis.call("del", KEYS[1]) 
else 
    return 0 
end

如果key对应的value一致,则删除这个key,通过这个方式释放锁是为了避免Client释放了其他Client申请的锁

到此你已经彻底理解了该如何实现一个分布式锁了,以及为什么要这样做的原因

加锁执行命令

SET resource_name random_value NX PX 30000

解锁执行脚本

if redis.call("get", KEYS[1]) == ARGV[1] then 
    return redis.call("del", KEYS[1]) 
else 
    return 0 
end

一个分布式锁的工具类写法如下

public class LockUtil {

    private static final String OK = "OK";
    private static final Long LONG_ONE = 1L;
    private static final String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    public static boolean lock(String lockKey, String requestId, long expire) {
        Jedis jedis = RedisPool.getJedis();
        SetParams setParams = new SetParams();
        setParams.nx().px(expire);
        return OK.equals(jedis.set(lockKey, requestId, setParams));
    }

    public static boolean unlock(String lockKey, String requestId) {
        Jedis jedis = RedisPool.getJedis();
        return LONG_ONE.equals(jedis.eval(script, 1, lockKey, requestId));
    }
}

如何正确的加解锁?

错误的加解锁逻辑

public void workV1() {
    String lockKey = "testKey";
    String requestId = UUID.randomUUID().toString();
    if (LockUtil.lock(lockKey, requestId, 2000)) {
        try {

            // 执行业务逻辑

            LockUtil.unlock(lockKey, requestId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个例子的加解锁都有问题

解锁:当发生异常的时候,解锁逻辑并不会执行,所以需要将其放在finally语句中
加锁:将加锁的命令成功发到服务端并成功执行,但是获取响应超时,就会执行解锁的逻辑。因此需要将加锁的逻辑放在try语句中

正确的加解锁逻辑

public void workV2() {
    String lockKey = "testKey";
    String requestId = UUID.randomUUID().toString();
    try {
        if (LockUtil.lock(lockKey, requestId, 2000)) {

            // 执行业务逻辑

        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        LockUtil.unlock(lockKey, requestId);
    }
}

参考博客

[1]https://mp.weixin.qq.com/s/8fdBKAyHZrfHmSajXT_dnA
[2]https://wudashan.cn/2017/10/23/Redis-Distributed-Lock-Implement/
红锁
[3]https://mp.weixin.qq.com/s/5E9_CSpnvf_KnDnKO0XjQg
[4]https://segmentfault.com/a/1190000039362581

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:21:05  更:2022-03-03 16:24:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:55:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码