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 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 2022-2-6 Redis作分布式锁 -> 正文阅读

[大数据]2022-2-6 Redis作分布式锁

作者:token keyword

【江南一点雨】的教程~
分布式锁可以限制程序的并发执行:执行思路: 进来一个线程先占位,当别的线程进来操作时,发现已经有人占位了,就会放弃或者稍后再试。(不会抢占)

在Redis中,占位一般用setnx(没有才存,有就不存),先进来的线程先占位,线程的操作执行完成后,再调用del指令释放位置。

package org.kk.distributed_lock;

import org.kk.jedis.Redis;

/**
 * @program: jedis
 * @description:
 * @author: zjx
 * @create: 2022-02-06 00:02
 **/
public class LockTest {
    public static void main(String[] args) {
        Redis redis=new Redis();
        redis.execute(jedis -> {
            Long setnx=jedis.setnx("k1","v1");
            if(setnx==1){
                //给锁添加过期时间,防止应用在运行过程中抛出异常导致锁无法得到及时释放
                jedis.expire("k1",5);
                //没人占位
                jedis.set("name","value");
                String name=jedis.get("name");
                System.out.println(name);
                jedis.del("k1");//删除k1,释放位置
                //如果代码业务执行中抛异常或者挂了,会导致del指令没有被调用。k1无法释放,后面来的请求全部堵塞在这里,锁也永远得不到释放。
                //解决方法:给锁加一个过期时间,确保锁在一定的时间之后,能够得到释放
            }else{
                //有线程占位 停止/暂缓操作

            }
        });
    }
}

改造后还有问题:如果在获取锁和设置过期时间之间服务器突然挂掉,锁还是被占用,无法及时得到释放,也会造成死锁,因为获取锁和设置过期时间是两个操作,不具备原子性。
Redis 2.8开始,setnx和expire可以通过一个命令来一起执行了,我们对上述代码进行改进:

package org.kk.distributed_lock;

import org.kk.jedis.Redis;
import redis.clients.jedis.params.SetParams;

/**
 * @program: jedis
 * @description:
 * @author: zjx
 * @create: 2022-02-06 00:02
 **/
public class LockTest {
    public static void main(String[] args) {
        Redis redis=new Redis();
        redis.execute(jedis -> {
        jedis.auth("123456");
           // Long setnx=jedis.setnx("k1","v1");
            String set = jedis.set("k1", "v1", new SetParams().nx().ex(5));
            if(set!=null&&"OK".equals(set)){
                //没人占位
                jedis.set("name","value");
                String name=jedis.get("name");
                System.out.println(name);
                jedis.del("k1");//删除k1,释放位置
                //如果代码业务执行中抛异常或者挂了,会导致del指令没有被调用。k1无法释放,后面来的请求全部堵塞在这里,锁也永远得不到释放。
                //解决方法:给锁加一个过期时间,确保锁在一定的时间之后,能够得到释放
            }else{
                //有线程占位 停止/暂缓操作

            }
        });
    }
}

解决超时问题

为了防止业务代码在执行时抛出异常,我们给每个锁添加了超时时间,超时以后,锁会被自动释放,但也带来了新问题:如果要执行的业务非常耗时,可能会出现紊乱。

第一个线程的任务还没执行成功锁就被释放,第二个线程会获取到锁开始执行…

  1. 尽量避免在获取锁之后执行耗时操作
  2. 将锁的value设置为随机字符串,每次释放锁的时候,都去比较随机字符串是否一致,如果一致再去释放。否则不释放。

对于第二种方案,释放锁的时候,先去查看锁的value,第二个比较value的值是否正确,第三步释放锁。有三个步骤,不具备原子性,所以得引入Lua脚本

Lua脚本的优势

  1. 使用方便,Redis中内置了对Lua脚本的支持
  2. Lua脚本可以在Redis服务端原子的执行多个Redis命令
  3. 由于网络在很大程度上会影响到Redis性能,而使用Lua脚本可以让多个命令一次执行,可以有效解决网络给Redis带来的性能问题。

在Redis中,使用Lua脚本,大致两种思路:
4. 提前在Redis服务端写好脚本,然后Java客户端去调用脚本
5. 可以直接在Java段写Lua脚本,写好之后,需要执行时,将脚本发送到Redis中去
第一步:
在这里插入图片描述

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

逻辑:得到锁的值和传进来的一样,那么释放锁。

第二步
可以给Lua脚本求一个SHA1和,命令如下:

将lua脚本加载到缓存

cat releasewhereValueequal.lua |redis-cli -a 123456 script load --pipe

在这里插入图片描述

script load这个命令会在Redis服务器中缓存Lua脚本,并返回脚本内容的 SHA1校验和,然后在Java端调用时,传入SHA1校验和作为参数,这样redis服务端就知道执行哪个脚本了。

public class LuaTest {
    public static void main(String[] args) {
        Redis redis=new Redis();
        redis.execute(jedis -> {
            jedis.auth("123456");
            //1 获取随机字符串
            String value = UUID.randomUUID().toString();
            //2 获取锁
            String k1 = jedis.set("k1", value, new SetParams().nx().ex(5));
            //3 判断是否成功拿到锁
            if(k1!=null&&"OK".equals(k1)){
                //4 具体业务
                jedis.set("site","www.kk.com");//随便干点什么
                String site = jedis.get("site");
                System.out.println(site);
                //5 释放锁
                jedis.evalsha("b8059ba43af6ffe8bed3db65bac35d452f8115d8", Arrays.asList("k1"),Arrays.asList(value)); //sha值,需要的参数
            }else{
                System.out.println("没拿到锁");
            }
        });
    }
}
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-02-07 13:47:24  更:2022-02-07 13:47:34 
 
开发: 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 13:37:05-

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