当前位置: 首页 > news >正文

分布式锁从0到1落地实现01(mysql/redis/zk)

1 准备数据库表

CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO user (id, name, age, email) VALUES
(1, '杨幂', 18, 'yangmi@baomidou.com'),
(2, '刘亦菲', 20, 'yifei@baomidou.com'),
(3, '刘德华', 28, 'andy@baomidou.com'),
(4, '李嘉欣', 21, 'candy@baomidou.com'),
(5, '张国荣', 24, 'brother@baomidou.com');

– DestributeLock.stock definition

CREATE TABLE stock (
id bigint NOT NULL AUTO_INCREMENT,
product_code varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
ware_house varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
count int DEFAULT 0,
version int DEFAULT 0,
PRIMARY KEY (id),
KEY stock_product_code_IDX (product_code) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2 官网

https://baomidou.com/pages/24112f/#%E6%A1%86%E6%9E%B6%E7%BB%93%E6%9E%84
https://baomidou.com/pages/223848/#keysequence
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis/3.2.3

3 本地JVM锁失效的情况以及解决方案

   1:  //本地线程锁失效的案例 1 使用多例模式 并且指定代理模式为  CGLIB// @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)2:  @Transactionalpublic synchronized void reduce() {........}或     @Transactionalpublic void reduce() {lock.lock();。。。。}由于事务注解 是是使用 AOP 来使用的,多线程情况下 在第一个线程还没提交事务之前 第二个先线程获取锁并且 在第一个线程之前执行完业务 并且提交完成 ,这时 第一个线程才 把数据提交 ,这样就会导致 两个线程修改之后的数据都是一样的 ,就会产生并发问题3: 在集群环境下,由于 请求负载到不同的 进程的服务了 ,这时jvm 的 事务也被隔离了 ,本地锁也只能管到自己了
===========================================================================4: 解决上述问题的方案一
使用数据库本身的锁,使用一个sql 在执行修改库存的时候 带上具体的查询条件public void reduce() {stockMapper.updateStock("1001",1);}@Update("update stock set count=#{count} where product_code=#{productCode} and count >= #{count}")int updateStock(@Param("productCode") String productCode, @Param("count") Integer count);缺点:这种方案没办法获取到锁修改前后的状态同一个商品有多个库存的时候没法判断修改哪一个所得范围是表锁5: 悲观锁   没加索引的时候锁范围还或者是全表,会有时所问题,加锁时枷锁的顺序要一致![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/74ce74a902554a67a0a57dd4945e72a7.png)@Transactionalpublic void reduce() {List<Stock> stocks = stockMapper.queryStockList("1001");Stock stock = stocks.get(0);if(stock != null && stock.getCount() > 0){stock.setCount(stock.getCount() -1);stockMapper.updateById(stock);}}@Select("select * from stock where product_code=#{productCode} for update")List<Stock> queryStockList(@Param("productCode") String productCode);添加索引之前的性能![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f629813ae41641faaf8e7c99480b4158.png)
添加索引之后的性能
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cdba8ecd841f4d4ebac0be1fed031801.png)6:乐观锁  添加时间戳或者版本号 +  CAS 操作来实现 compare and swap 比较并交换
select * from stock where product_code ='10010' 
拿到version 字段
判断库存大于要购买的数量N
update stock set count = count -n ,version = 上一步查询出来的version + 1 and version= 上一步查询出来的version
如果影响的行为0 说明已经被别人改过了,需要循环或者递归重试@Transactionalpublic void reduce() {List<Stock> stocks = stockMapper.selectList(new QueryWrapper<Stock>().eq("product_code", "1001"));Stock stock = stocks.get(0);Integer version = stock.getVersion();if (stock != null && stock.getCount() > 0) {stock.setCount(stock.getCount() - 1);stock.setVersion(version + 1);if (stockMapper.update(new UpdateWrapper<Stock>().eq("id", stock.getId()).eq("version", stock.getVersion())) == 0) {reduce();}}}
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/91ad0c4c8e124209be59b1321b6fe428.png)

4 redis 版本的锁

1: 添加依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>3.2.3</version></dependency>
2: 修改配置文件
spring.data.redis.host=192.168.187.128
spring.data.redis.port=6379
spring.data.redis.database=0
spring.cache.type=REDIS
spring.data.redis.timeout=10000
spring.data.redis.lettuce.pool.max-active=100
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
spring.data.redis.lettuce.pool.time-between-eviction-runs=10s编写一个测试用例测试是否redis正确连接此时可能会出现 连不上redis ,需要修改配置文件
注释 掉 bind 127.0.0.1 这个项
可以连接上之后 关闭服务器再打开会发现我们set 的 值消失了 这是因为我们没有开启持久化
可以去开启 rdb 或者 aof 我这里开启了 aof 
在配置文件中将 appendonly 的 no 改为 yes 
重启服务  ./redis-server redis.conf
现在去运行代码可以看到 正常运行结果了,环境搭建完成
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ba77bd876acf40998d981548d6a2669c.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5ee09450d4794419b8949d52ff7daefb.png)3: 使用 redis 自带的乐观锁来实现锁结局超卖问题
可以保证线程安全问题但是,但是并发量非常低,性能不能保证,不推荐使用public void reduce() {this.redisTemplate.execute(new SessionCallback<Object>() {@Overridepublic  Object execute(RedisOperations operations) throws DataAccessException {operations.watch("stock"); //监控节点String stock = operations.opsForValue().get("stock")==null?"": operations.opsForValue().get("stock").toString();//判断库存是否充足if(StringUtils.isNoneBlank(stock) && Integer.parseInt(stock) != 0){Integer st = Integer.parseInt(stock);if(st > 0){operations.multi(); //开启事务//扣减库存operations.opsForValue().set("stock",String.valueOf(--st));//执行事务List exec = operations.exec();if(exec == null || exec.size()==0){//如果返回为空则表示扣减库存失败需要重试try {Thread.sleep(20);} catch (InterruptedException e) {throw new RuntimeException(e);}reduce();}return exec;}}return null;}});}

相关文章:

分布式锁从0到1落地实现01(mysql/redis/zk)

1 准备数据库表 CREATE TABLE user ( id bigint(20) NOT NULL COMMENT 主键ID, name varchar(30) DEFAULT NULL COMMENT 姓名, age int(11) DEFAULT NULL COMMENT 年龄, email varchar(50) DEFAULT NULL COMMENT 邮箱, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;I…...

安全运营方案的基本框架和关键要素

一、前言 阐述安全运营方案的目的和重要性。强调安全运营与组织整体战略目标的关联。 二、安全运营原则 确立安全运营的基本原则&#xff0c;如保密性、完整性和可用性。明确安全责任划分&#xff0c;确保各部门和人员履行安全职责。 三、安全风险评估与管理 进行全面的安…...

用C语言执行SQLite3的gcc编译细节

错误信息&#xff1a; /tmp/cc3joSwp.o: In function main: execSqlite.c:(.text0x100): undefined reference to sqlite3_open execSqlite.c:(.text0x16c): undefined reference to sqlite3_exec execSqlite.c:(.text0x174): undefined reference to sqlite3_close execSqlit…...

matlab双目相机标定-需要什么参数、怎么获得

相机标定目的&#xff1a;获得相机内参、外参、畸变系数&#xff0c;摄像头的内参(f,1/dx,1/dy,cx,cy)、畸变参数(k1,k2,k3,p1,p1)和外参(R,t)&#xff0c;用于接下来的双目校正和深度图生成从而实现二维到三维的转换。 相机标定方法&#xff1a;opencv 双目相机标定以及立体…...

大型语言模型的智能助手:检索增强生成(RAG)

背景 在人工智能的浪潮中&#xff0c;大型语言模型&#xff08;LLMs&#xff09;如GPT系列和LLama系列在自然语言处理&#xff08;NLP&#xff09;领域取得了显著成就。它们能够完成复杂的语言任务&#xff0c;如文本摘要、机器翻译、甚至创作诗歌。然而&#xff0c;这些模型在…...

Ubuntu 安装谷歌拼音输入法

一、Fcitx 安装 在Ubuntu 下&#xff0c;谷歌拼音输入法是基于Fcitx输入法的。所以&#xff0c;首先需要安装Fcitx。一般来说&#xff0c;Ubuntu最新版中都默认安装了Fcitx&#xff0c;但是为了确保一下&#xff0c;我们可以在系统终端中运行如下命令&#xff1a; sudo apt ins…...

修改MonkeyDev默认配置适配Xcode15

上一篇文章介绍了升级Xcode15后&#xff0c;适配MonkeyDev的一些操作&#xff0c;具体操作可以查看&#xff1a;Xcode 15 适配 MonkeyDev。 但是每次新建项目都要去修改那些配置&#xff0c;浪费时间和精力&#xff0c;这篇文章主要介绍如何修改MonkeyDev的默认配置&#xff0…...

deepinlinux打包deb文件完善

最近学习了一篇qt入门文章&#xff0c;做了一个小应用&#xff0c;要给另一台电脑用时发现还需考虑另一台没有qt&#xff0c;要把相关库带过去&#xff0c;后来就学了打包成deb安装包&#xff0c;看起来更专业。 win下搜索qt依赖库的程序是windeployqt&#xff0c;先将应用输出…...

Android studioSDK集成:com.yechaoa.yutilskt

文章目录 1、工具介绍2、集成 1、工具介绍 com.yechaoa.yutilskt是一个Android开发工具库&#xff0c;提供了一些常用的工具类和方法&#xff0c;方便开发者进行Android应用程序的开发。该库包含了以下功能&#xff1a; 网络请求工具类&#xff1a;提供了简化的网络请求方法&…...

openssl3.2 - exp - PEM <==> DER

文章目录 openssl3.2 - exp - PEM <> DER概述笔记加密用的私钥(带口令保护) - PEM > DER加密用的私钥(不带口令保护) - DER > PEM将不带口令的PEM转成带口令的PEM支持口令的算法备注END openssl3.2 - exp - PEM <> DER 概述 想将客户端私钥 服务端公钥 数…...

云计算的部署方式(公有云、私有云、混合云、社区云)

云计算的部署方式(公有云、私有云、混合云、社区云) 目录 零、00时光宝盒 一、云计算的部署方式 1.1、公有云&#xff08;Public Cloud&#xff09; 1.2、私有云&#xff08;Private Cloud&#xff09;  1.3、混合云&#xff08;Hybrid Cloud&#xff09; 1.4、社区云&am…...

umi4 项目使用 keepalive 缓存页面(umi-plugin-keep-alive、react-activation)

umi4使用keepalive 配置文件config\config.ts export default defineConfig({plugins: [umi-plugin-keep-alive], });安装add umi-plugin-keep-alive yarn add umi-plugin-keep-alive页面 A import { KeepAlive, history, useAliveController } from umijs/max; const Page…...

new;getline();重载<<和>>

面向对象程序设计的优点&#xff1a; 易维护易扩展模块化&#xff1a;通过设置访问级别&#xff0c;限制别人对自己的访问&#xff0c;保护了数据安全 int main(){ return 0;} 返回值0在windows下编程一般没用&#xff0c;但是在linux中编程&#xff0c;返回值有时有用 汇编与…...

python报错

Missing optional dependency ‘xlrd’. Install xlrd > 1.0.0 for Excel support Use pip or conda to install xlrd 安装xlrd库...

Android14 Handle机制

Handle是进程内部, 线程之间的通信机制. handle主要接受子线程发送的数据, 并用此数据配合主线程更新UI handle可以分发Message对象和Runnable对象到主线程中, 每个handle实例, 都会绑定到创建他的线程中, 它有两个作用,: (1) 安排消息在某个主线程中某个地方执行 (2) 安排…...

spark 总结

1.spark 配置文件 spark-default.conf spark.yarn.historyServer.address xiemeng-01:18080 spark.history.port18080 hive-site.xml <configuration><property><name>javax.jdo.option.ConnectionURL</name> </property><property>&…...

Gitlab修改仓库权限为public、Internal、Private

Public&#xff08;公开&#xff09;&#xff1a;所有人都可以访问该仓库&#xff1b; Internal&#xff08;内部&#xff09;&#xff1a;同一个GitLab群组或实例内的所有用户都可以访问该仓库&#xff1b; Private&#xff08;私人&#xff09;&#xff1a;仅包括指定成员的用…...

Python语言例题集(008)

#!/usr/bin/python3 #建立链表类和遍历此链表 class Node(): def init(self,dataNone): self.datadata self.nextNone class LinkedList(): def init(self): self.headNone def printList(self):ptrself.headwhile ptr:print(ptr.data)ptrptr.nextlinkLinkedList() link.he…...

【Java核心能力】京东并行框架asyncTool如何针对高并发场景进行优化?

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…...

如何在Mapbox GL中处理大的GEOJSON文件

Mapbox GL可以将 GeoJSON 数据由客户端(Web 浏览器或移动设备)即时转换为 Mapbox 矢量切片进行显示和处理。本文的目的是教大家如何有效加载和渲染大型 GeoJSON 源,并优化渲染显示速度,增强用户体验,减少客户端卡顿问题。本文以Mapbox 为例,至于其它框架原理大致相同,可…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...