分布式锁之-redis

什么是分布式锁?
即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。
分布式锁应该具备哪些条件?
1:在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
2:高可用的获取锁与释放锁
3:高性能的获取锁与释放锁
4:具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
5:具备锁失效机制,即自动解锁,防止死锁
分布式锁的实现方式有那些?
1.使用关系型mysql数据库实现分布式锁。
2.使用redis非关系型数据库实现分布式锁。
3.使用zookeeper注册中心来实现分布式锁。
本文将详细介绍重要的分布式锁–给予redis的分布式锁。
A:Redisson 实现的分布式锁使用演示
B:自己实现的 Redis 分布式锁使用演示
数据库脚本

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for product_stock
-- ----------------------------
DROP TABLE IF EXISTS `product_stock`;
CREATE TABLE `product_stock`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`stock` int(11) NULL DEFAULT 0,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '产品库存表\n' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of product_stock
-- ----------------------------
INSERT INTO `product_stock` VALUES (1, 20);SET FOREIGN_KEY_CHECKS = 1;

yml配置信息

spring:application:name: lock-redis# 数据库链接 要改成自己的数据链接信息datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://${MYSQL_URL:127.0.0.1}:3306/lock-test?autoReconnect=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=trueusername: rootpassword: ${MYSQL_PASSWORD:abc123}hikari:max-lifetime: 500000# redis链接,要改成自己的链接配置redis:host: ${REDIS_URL:127.0.0.1}port: 6379password: ${REDIS_PASSWORD:abc123}database: 11

自己实现的分布式锁的方式。

  /*** 模拟减库存操作 - 自己实现 redis 锁接口** @return str*/@GetMapping("/reduceStockByMyLock/{id}")public String reduceStockByMyLock(@PathVariable("id") Integer id) {return productStockService.reduceStockByMyLock(id);}
 @SneakyThrows@Overridepublic String reduceStockByMyLock(Integer id) {// requestId 确保每一个请求生成的都不一样,这里使用 uuid,也可以使用其他分布式唯一 id 方案String requestId = UUID.randomUUID().toString().replace("-", "");int expireTime = 10;bulkRedisLock.lock(requestId, expireTime);// 开启续命线程,Thread watchDog = bulkRedisLock.watchDog(expireTime, requestId);watchDog.setDaemon(true);watchDog.start();try {ProductStock stock = productStockMapper.selectById(id);if (stock != null && stock.getStock() > 0) {productStockMapper.reduceStock(id);} else {throw new RuntimeException("库存不足!");}} finally {watchDog.interrupt();bulkRedisLock.unlock(requestId);}return "ok";}

自己实现的配置锁BulkRedisLock

@Slf4j
@Component
@SuppressWarnings("all")
public class BulkRedisLock {private static final String LOCK_PREFIX = "redisLock";@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 尝试获取锁** @param requestId  请求id* @param expireTime 过期时间  单位毫秒* @return true false*/public boolean lock(String requestId, int expireTime) {// 也可以使用 lua 脚本 "return redis.call('set',KEYS[1], ARGV[1],'NX','PX',ARGV[2])"// 使用redis保证原子操作(判断是否存在,添加key,设置过期时间)while (true) {if (Boolean.TRUE.equals(stringRedisTemplate.boundValueOps(LOCK_PREFIX).setIfAbsent(requestId, expireTime, TimeUnit.SECONDS))) {return true;}}}/*** 将锁释放掉* <p>* 为何解锁需要校验 requestId 因为不是自己的锁不能释放* 客户端A加锁,一段时间之后客户端A解锁,在执行 lock 之前,锁突然过期了。* 此时客户端B尝试加锁成功,然后客户端A再执行 unlock 方法,则将客户端B的锁给解除了。** @param requestId 请求id* @return true false*/public boolean unlock(String requestId) {// 这里使用Lua脚本保证原子性操作String script = "if  redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else return 0 end";DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);Long res = stringRedisTemplate.execute(redisScript, Collections.singletonList(LOCK_PREFIX), requestId);return new Long(1).equals(res);}/*** 创建续命子线程** @param time      操作预期耗时* @param requestId 唯一标识* @return 续命线程 Thread*/public Thread watchDog(int time, String requestId) {return new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(time * 2 / 3);//重置时间String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('expire', KEYS[1],ARGV[2]) " +"else return '0' end";List<Object> args = new ArrayList();args.add(requestId);args.add(time);DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);stringRedisTemplate.execute(redisScript, Collections.singletonList(LOCK_PREFIX), args);} catch (Exception e) {// sleep interrupted 是因为 sleep// log.info("watchDog异常:{}", e.getMessage());return;}}});}
}

mapper的xml文件

 <update id="reduceStock">update product_stockset stock = stock - 1where id = #{id}</update>

控制层入库contoller

 /*** 模拟减库存操作 - 自己实现 redis 锁接口** @return str*/@GetMapping("/reduceStockByMyLock/{id}")public String reduceStockByMyLock(@PathVariable("id") Integer id) {return productStockService.reduceStockByMyLock(id);}

使用edisson 实现的分布式锁使用
控制controller

 /*** 模拟减库存操作 - redisson 实现** @return str*/@GetMapping("/reduceStock/{id}")public String reduceStockByRedisson(@PathVariable("id") Integer id) {return productStockService.reduceStock(id);}

业务实现

@Overridepublic String reduceStock(Integer id) {RLock lock = redissonClient.getLock("lock");lock.lock();try {ProductStock stock = productStockMapper.selectById(id);if (stock != null && stock.getStock() > 0) {productStockMapper.reduceStock(id);} else {throw new RuntimeException("库存不足!");}} finally {lock.unlock();}return "ok";}

以上的是分布式锁之-redis 若需完整代码 可识别二维码后 给您发代码。
若友友们有更好的分布式锁的实现方式 请在评论区留下你可贵的分享 谢谢!!!!
在这里插入图片描述

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

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

相关文章

VscodeC/C++环境配置

引言 vscode是一款非常好用的编辑器&#xff0c;集成了大量的插件&#xff0c;具有很高的自由度&#xff0c;因此广受大家的喜爱。但是他本身是不带编译器的&#xff0c;因此如果要使用vscode来编译C/C程序的话&#xff0c;我们需要额外安装编译器并且为vscode配上环境。 编译…

Observability:监控与可观察性不同的 3 个原因

作者&#xff1a;来自 Elastic Elastic Observability Team 监控和可观察性通常可以互换使用&#xff0c;但它们并不完全相同。 监控是可观察性的重要组成部分&#xff0c;但可观察性远远超出了传统监控实践的范围。 主要区别&#xff1a;监控从各个组件收集数据 —— 时间和内…

堆的基本操作(c语言实现)

1.堆的基本操作 1.1定义堆 typedef int HPDataType;//堆中存储数据的类型typedef struct Heap {HPDataType* a;//用于存储数据的数组int size;//记录堆中已有元素个数int capacity;//记录堆的容量 }HP;1.2初始化堆 然后我们需要一个初始化函数&#xff0c;对刚创建的堆进行初…

Element-plus修改input的placeholder文字颜色

需求 代码 .el-input__inner::placeholder {color: #666f8d !important; }

vs 2022 Xamarin 生成 Android apk

再保存&#xff0c;如果没有生成apk就重启软件 再试一次

软件测试小妙招:详细解读 postman接口测试导入导出操作

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman中的集合脚本&#xff0c;环境变量、全局变量全部都可以导出&#xff0c;然后分享给团队…

Python开源工具库使用之运动姿势追踪库mediapipe

文章目录 前言一、姿势估计1.1 姿态关键点1.2 旧版 solution API1.3 新版 solution API1.4 俯卧撑计数 二、手部追踪2.1 手部姿态2.2 API 使用2.3 识别手势含义 参考 前言 Mediapipe 是谷歌出品的一种开源框架&#xff0c;旨在为开发者提供一种简单而强大的工具&#xff0c;用…

三.搜索与图论(未完结)

DFS(深搜) 之前写过三篇关于dfs的 练习总结: 基础算法--递归搜索DFS练习总结(上)-CSDN博客 基础算法--递归搜索DFS练习总结(中)-CSDN博客 基础算法--递归搜索DFS练习总结(下)-CSDN博客 以下题目均为 补充练习: P1460 [USACO2.1] 健康的荷斯坦奶牛 Healthy Holsteins …

【华为】AC直连二层组网隧道转发实验配置

【华为】AC直连二层组网隧道转发实验配置 实验需求拓扑配置AC数据规划表 AC的配置顺序AC1基本配置(二层通信)AP上线VAP组关联--WLAN业务流量 LSW1AR1STA获取AP的业务流量 配置文档 实验需求 AC组网方式&#xff1a;直连二层组网。 业务数据转发方式&#xff1a;隧道转发。 DHC…

MacOS搭建docker本地私有镜像库

相关环境 macOS: bigsur 11.7.8 docker desktop: 4.22.0 docker engine: 24.0.5 准备工作 本机已经安装好docker desktop&#xff0c;未安装的自行参考其他教程。如果不能翻墙&#xff0c;可以修改本地的镜像地址&#xff0c;可在docker desktop 设置中的docker engine中修…

Excel Module: Iteration #1 EasyExcel生成下拉列表模版时传入动态参数查询下拉数据

系列文章 EasyExcel生成带下拉列表或多级级联列表的Excel模版自定义校验导入数据(修订) 目录 系列文章前言仓库一、实现1.1 下拉元数据对象1.2 构建下拉元数据的映射关系1.3 框架方式1.3.1 框架实现1.3.2 框架用例模版类加载下拉业务导出接口 1.4 EasyExcel方式1.4.1 EasyExce…

Redis(Jedis和SpringBoot整合Redis)

文章目录 1.Jedis1.介绍2.环境配置1.创建maven项目2.pom.xml引入依赖3.新建一个包并创建一个文件 3.Jedis远程连接到Redis1.Redis放到服务器可以连接的前提条件2.为Redis设置密码1.编辑配置文件2.找到 requirepass3.设置密码为root4.重启Redis&#xff0c;在shutdown的时候报错…

计算机网络——Dijkstra路由算法

实验目的 实现基于 Dijkstra 算法的路由软件 实验内容 网络拓扑如图所示 实验过程 先编写开辟应该图的空间&#xff0c;然后给点映射数字&#xff0c;构建图。程序获取用户输入的学号&#xff0c;构建图中边的权值。接下来程序从用户输入获取最短路径的搜索起点&#xff0…

基于C++基础的函数模块

在C中&#xff0c;函数是一段封装了某种功能的代码块&#xff0c;可以在程序的不同地方重复使用。函数定义包含如下组成部分&#xff1a; 函数头&#xff1a;函数头包括函数返回类型、函数名和参数列表。函数返回类型规定了函数返回的数据类型&#xff0c;函数名是函数的唯一标…

Java_从入门到JavaEE_11

一、抽象类及抽象方法 1.认识抽象类及抽象方法 应用场景&#xff1a;当一个方法必须在父类中出现&#xff0c;但是这个方法又不好实现&#xff0c;就把该方法变成抽象方法&#xff0c;交给非抽象的子类去实现 实例&#xff1a; //抽象类 public abstract class 类名{//抽象方…

5月将有17款游戏发布,腾讯的《地下城与勇士:起源》备受关注

易采游戏网5月8日消息&#xff0c;本月将有17款新游戏预计上线&#xff0c;其中14款已正式定档&#xff0c;游戏市场即将迎来一场盛大的狂欢。在众多备受期待的游戏中&#xff0c;有两款游戏尤其引人注目&#xff0c;它们分别是来自库洛和腾讯的《地下城与勇士&#xff1a;起源…

学习方法的重要性

原贴&#xff1a;https://www.cnblogs.com/feily/p/13999204.html 原贴&#xff1a;https://36kr.com/p/1236733055209095 1、 “一万小时定律”的正确和误区 正确&#xff1a; 天才和大师的非凡&#xff0c;不是真的天资超人一等&#xff0c;而是付出了持续不断的努力&…

武汉星起航:成功挂牌上股交,优势尽显启新程,共绘创业投资梦

在金秋十月的尾声&#xff0c;武汉星起航电子商务有限公司迎来了一个重要的历史时刻——于2023年10月30日在上海股权托管交易中心成功挂牌展示&#xff0c;正式登陆资本市场。这一里程碑式的跨越&#xff0c;不仅标志着武汉星起航在跨境电商领域的卓越实力&#xff0c;更彰显了…

MAC地址冲突案例

1、问题描述&#xff1a;WiFi-A网段做了策略路由&#xff0c;引流到另一台设备&#xff0c;连接WiFi-A后通过DHCP获取到了地址却无法上网&#xff0c;此时排查思路是什么&#xff1f; &#xff08;1&#xff09;、排查方法&#xff1a; 看到网关通信是否正常 第一次获取地址正…

mysql中varchar与bigint直接比较会导致精度丢失以至于匹配到多行数据

在mysql中&#xff0c;我们都知道如果一个索引字段使用了函数或者计算那么查询的时候索引会失效&#xff0c;可是我相信在联表的时候我们只会关注两个表关联字段是否都创建了索引&#xff0c;却没有关注过这两个字段的类型是否一致&#xff0c;如果不一致的话索引是会失效的&am…