Learn Redis 5 (Java)

分布式锁

在面对高并发业务时,单个项目解决不过来,此时一个项目部署到多个机器,这就是集群模式,不同的项目实例就会对应不同的端口和JVM。

1.模拟集群模式        

Nginx实现负载均衡(轮询)

2.使用集群模式下的问题 

使用集群模式,不同的项目则对应不同的JVM,而锁监视器是基于JVM的,这是就会存在线程安全问题,代码相同,申请的却不是同一把锁,一把是JVM1的,另一把是JVM2的。

在解决一人抢购多个商品业务(限购)时,出现业务数据异常:

同一个人连续发送两次请求,一个到了8081、另一个在8082,都能获取到锁,查询得到的也是同一个结果,导致两个不同实例线程都能购买成功。

3.分布式锁 

造成这个的原因是锁不是共享的,且不可视,JVM1看不到JVM2的锁。

解决:使他们申请的是同一把锁,实现多线程之间互斥

使用分布式锁,使不同的JVM统一锁监视器 

 4.使用redis实现分布式锁

使用逻辑和Lock基本一致,但是获取锁失败后不会等待而是返回false,且添加了超时释放

    public boolean tryLock(String key){Boolean flat = redisTemplate.opsForValue().setIfAbsent(key, "1", TTL, TimeUnit.SECONDS);return flat;}public void unLock(String key){redisTemplate.delete(key);}

问题 

1.锁被误删

存在线程1 tryLock()成功后,执行业务逻辑,但是发生了业务阻塞,就卡在那了,导致没有手动的 unLock(),而是因为超时而释放了,

此时线程2进来了 ,tryLock()成功后,执行业务逻辑,线程1突然好了,就去释放锁了,但是此时的锁不是线程1的,而是线程2的,线程2执行完后很懵逼,家怎么被偷了,我该释放谁啊?

解决:释放锁时增加判断,锁是自己的吗 

    public boolean tryLock(Long expireTime){// 获取当前线程id// 在集群中,线程id由所在jvm管理,所以线程id会重复long id = Thread.currentThread().getId();String value = uuid+id;// 设置锁Boolean succeed = stringRedisTemplate.opsForValue().setIfAbsent(prefix + name, value, expireTime, TimeUnit.SECONDS);// 拆箱return Boolean.TRUE.equals(succeed);}public void unLock(){// 获取当前线程valueString value = stringRedisTemplate.opsForValue().get(prefix + name);// 判断是否是自己的锁long id = Thread.currentThread().getId();String myValue = uuid+id;if (value.equals(myValue)){stringRedisTemplate.delete(prefix + name);}}

 这就行了吗,会不会有更加极端的情况,卡在了stringRedisTemplate.delete(prefix + name),判断锁逻辑已经成功,要删除时,业务阻塞了,又发生上面的锁被误删情况。

原因是这个操作不是原子性的,所以存在中途发生问题,只需要把操作写到一个脚本

2.Lua脚本

使用redis命令调用Lua脚本Lua 教程 | 菜鸟教程

基本全局变量a=10,局部变量local b=10,方法function,调用redis redis.call()

1EVAL script numkeys key [key ...] arg [arg ...]
执行 Lua 脚本。
2EVALSHA sha1 numkeys key [key ...] arg [arg ...]
执行 Lua 脚本。
3SCRIPT EXISTS script [script ...]
查看指定的脚本是否已经被保存在缓存当中。
4SCRIPT FLUSH
从脚本缓存中移除所有脚本。
5SCRIPT KILL
杀死当前正在运行的 Lua 脚本。
6SCRIPT LOAD script
将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。

 主要看1,EVAL  script(脚本)numkeys(key参数数量)key[](key参数),arg[](其他参数)

写Lua脚本

IDEA下载EmmyLua插件,创建Lua脚本在resource路径下

if (redis.call('exists', KEYS[1]) == ARGV[1]) thenredis.call('del', KEYS[1])
end
return 0

 调用Lua脚本

    //定义释放锁的lua脚本private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;static{UNLOCK_SCRIPT = new DefaultRedisScript<>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);}public void unLock(){stringRedisTemplate.execute(UNLOCK_SCRIPT,//使用Collections.singletonList(),将key转换为listCollections.singletonList(prefix + name),uuid+Thread.currentThread().getId());}

5.Redisson

1.添加maven依赖
        <!--Redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.2</version></dependency>
2.配置Redisson客户端
@Configuration
public class RedisConfig {@Beanpublic RedissonClient redissonClient(){// 创建配置Config config = new Config();// 添加节点信息, ip:port和密码config.useSingleServer().setAddress("redis://localhost:6379").setPassword("123456");return Redisson.create(config);}
}
3.修改业务
        //使用redisson获取锁对象,可重入RLock lock = redissonClient.getLock("order:" + id);//尝试获取锁,尝试获取锁的时间最大等待时间为1s,超时释放时间20sboolean isLock = lock.tryLock(1,20L, TimeUnit.SECONDS);if (!isLock){return Result.fail("不允许重复下单");}try {//手动创建代理对象IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {lock.unlock();}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36099.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lua学习(三)

错误处理 assert断言 作用&#xff1a;确保某些数据是符合预期的&#xff0c;避免影响最终结果。 格式&#xff1a;assert(条件语句&#xff0c;报错信息) 当条件语句为true时&#xff0c;assert语句不会有任何行为&#xff0c;但是当为false时&#xff0c;assert会将报错信息…

基于eNSP的IPV4和IPV6企业网络规划

基于eNSP的IPV4和IPV6企业网络规划 前言网络拓扑设计功能设计技术详解一、网络设备基础配置二、虚拟局域网&#xff08;VLAN&#xff09;与广播域划分三、冗余协议与链路故障检测四、IP地址自动分配与DHCP相关配置五、动态路由与安全认证六、广域网互联及VPN实现七、网络地址转…

优选算法合集————双指针(专题四)

1&#xff0c;一维前缀和模版 题目描述&#xff1a; 描述 给定一个长度为n的数组a1,a2,....ana1​,a2​,....an​. 接下来有q次查询, 每次查询有两个参数l, r. 对于每个询问, 请输出alal1....aral​al1​....ar​ 输入描述&#xff1a; 第一行包含两个整数n和q. 第二行…

Web3游戏行业报告

一&#xff0c;gamefi经济 什么是gamefi GameFi是一个缩写&#xff0c;它结合了游戏和去中心化金融(“DeFi”)这两个术语&#xff0c;关注的是游戏玩法如何在去中心化系统中实现货币化。对于游戏而言&#xff0c;只要开放了交易市场&#xff0c;允许玩家自由买卖&#xff0c;…

【程序人生】成功人生架构图(分层模型)

文章目录 ⭐前言⭐一、根基层——价值观与使命⭐二、支柱层——健康与能量⭐三、驱动层——学习与进化⭐四、网络层——关系系统⭐五、目标层——成就与财富⭐六、顶层——意义与传承⭐外层&#xff1a;调节环——平衡与抗风险⭐思维导图 标题详情作者JosieBook头衔CSDN博客专家…

拖拽实现+摇杆实现

拖拽实现 拖拽事件实现: 半透明渐变贴图在ios设备下&#xff0c;使用压缩会造成图片质量损失&#xff0c;所以可以将半透明渐变UI切片单独制作真彩色图集 拖拽事件组 IBeginDragHandler:检测到射线后&#xff0c;当拖拽动作开始时执行一次回调函数 IDragHandler:拖拽开始后&a…

vs2017版本与arcgis10.1的ArcObject SDK for .NET兼容配置终结解决方案

因电脑用的arcgis10.1,之前安装的vs2010正常能使用AO和AE&#xff0c;安装vs2017后无法使用了&#xff0c;在重新按照新版本arcgis engine或者arcObject费时费力&#xff0c;还需要重新查找资源。 用vs2017与arc10.1的集成主要两个问题&#xff0c;1&#xff1a;安装后vs中没有…

C语言和C++到底有什么关系?

C 读作“C 加加”&#xff0c;是“C Plus Plus”的简称。 顾名思义&#xff0c;C 就是在 C 语言的基础上增加了新特性&#xff0c;玩出了新花样&#xff0c;所以才说“Plus”&#xff0c;就像 Win11 和 Win10、iPhone 15 和 iPhone 15 Pro 的关系。 C 语言是 1972 年由美国贝…

企业微信群聊机器人开发

拿到机器人hook 机器人开发文档 https://developer.work.weixin.qq.com/document/path/91770

AT指令集-NBIOT

是什么&#xff1f; 窄带物联网&#xff08;Narrow Band Internet of Things, NB-IoT&#xff09;成为万物互联网络的一个重要分支支持低功耗设备在广域网的蜂窝数据连接&#xff0c;也被叫作低功耗广域网(LPWAN)NB-IoT支持待机时间长、对网络连接要求较高设备的高效连接NB-Io…

网络爬虫【爬虫库urllib】

我叫不三不四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲爬虫 urllib介绍 Urllib是Python自带的标准库&#xff0c;无须安装&#xff0c;直接引用即可。 Urllib是一个收集几个模块来使用URL的软件包&#xff0c;大致具备以下功能。 ● urlli…

vue中js简单创建一个事件中心/中间件/eventBus

vue中js简单创建一个事件中心/中间件/eventBus 目录结构如下&#xff1a; eventBus.js class eventBus {constructor() {this.events {};}// 监听事件on(event, callback) {if (!this.events[event]) {this.events[event] [];}this.events[event].push(callback);}// 发射…

弹球小游戏-简单开发版

一、需求 弹球小游戏是一个简单的互动游戏&#xff0c;玩家需要控制一个挡板在窗口底部左右移动&#xff0c;以接住从上方落下的球。游戏的主要需求包括&#xff1a; (1) 游戏界面 &#xff1a;创建一个指定尺寸的游戏窗口&#xff0c;显示球和挡板。 (2) 球的运动 &#xf…

Cursor与Blender-MCP生成3D模型

随着DeepSeek的热度&#xff0c;各行各业接入AI智能&#xff0c;当然作为一个深受3D爱好者喜爱的软件——Blender&#xff0c;也接入了AI智能&#xff0c;通过Blender-MCP&#xff0c;开启一场Blender的智能化模型创建的世界之旅。 目录 1.准备工作2.环境配置2.1 Mac安装2.2 W…

简单以太网配置

display arp //查看路由器mac地址 交换机配置命令&#xff1a; system-view // 从用户视图进入系统视图 dis mac-address //查看mac地址表 路由器配置命令: system-view // 从用户视图进入系统视图 int GigabitEthernet 0/0/0 //进入G口 0/0/0 进入之后配置网关: ip addre…

SpringBoot可以同时处理多少请求?

大家好&#xff0c;我是锋哥。今天分享关于【SpringBoot可以同时处理多少请求&#xff1f;】面试题。希望对大家有帮助&#xff1b; SpringBoot可以同时处理多少请求&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring Boot 本身并不直接限制可以处…

一、初始 Linux

文章目录 一、操作系统概述二、Linux 初识1. Linux 的组成2. Linux 发行版 三、远程链接 Linux 系统1. 四、WSL (windows subsystem for linux)1. 什么是 WSL2. 如何下载 WSL3. 安装不同的 Linux 发行版4. 启动停止使用指定发行版5. 卸载与备份6. 文件共享7. 命令混用8. 用 vsc…

LogicFlow介绍

LogicFlow介绍 LogicFlow是一款流程图编辑框架&#xff0c;提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端自定义开发各种逻辑编排场景&#xff0c;如流程图、ER图、BPMN流程等。在工作审批流配置、机器人逻辑编排、无代码平…

Flask实时监控:打造智能多设备在线离线检测平台(升级版)

前言 武林之中&#xff0c;最讲究的便是“掌控”。若是手下弟子忽然失踪&#xff0c;若是江湖好友生死未卜&#xff0c;岂不令人寝食难安&#xff1f;今日&#xff0c;吾等化身技术侠客&#xff0c;祭出Flask实时监控大法&#xff0c;打造一款智能多设备在线离线检测平台&…

嵌入式编程优化技巧:do-while(0)、case范围扩展与内建函数

在嵌入式编程中,优化代码的性能和可靠性至关重要。无论是通过优化控制结构、提升代码的执行效率,还是利用编译器提供的内建函数来加速关键任务,开发者都需要掌握各种技巧和方法。本文将探讨三种在嵌入式编程中常用的优化技术:do-while(0)的使用、case范围扩展以及内建函数的…