分布式锁功效初探——以电商问题为例

news2024/11/26 0:44:18

文章目录

  • 电商库存问题
    • 单机处理-Sychronized
    • 多机器处理-分布式锁
      • 入门级别,用redis实现,setnx
        • 问题1:逻辑可能异常,造成死锁
        • 问题2:机器宕机
        • 问题3:锁一直失效,乱套
        • 锁续命
      • redisson
      • 分布式丢锁问题
        • 主从、分布式的情况下锁丢失
        • 红锁问题
      • 分布式锁性能优化
        • 锁的粒度越小越好
        • 分段锁
  • 电商可能存在问题(下单链路)
    • 订单重复生成
    • 付钱了,后台状态取消了
  • Future(成功监听处理)
  • Lua脚本(redisson内部加锁实现)
  • Apache JMeter工具(压测)

电商库存问题

redis里面存储库存
程序里面减
多并发可能会有问题(超卖)

单机处理-Sychronized

可以加锁解决(Sychronized),但是只能解决单机问题,在分布式多机器上用谨慎,很可能出BUG

多机器处理-分布式锁

前台Nginx,后台代码部署于多个tomcat,共用redis
多台机器,并发越多,超卖的情况就越明显

入门级别,用redis实现,setnx

setnx key value,当且仅当key不存在,否则会使用第一次的值,后续不会覆盖
利用这个特性去实现锁
因为redis核心命令执行是单线程,库存计算需要存redis,多次进来需要排队,根据是否存在指定key去判断

Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "aaa");
//用返回结果去判断锁
if(!result){
	return "系统正在执行"
}
// 执行业务逻辑
// 注意删除锁
stringRedisTemplate.delete("lockKey")
问题1:逻辑可能异常,造成死锁

加try catch,删除放到finaly

try{
	// 执行业务逻辑
}finally{
	stringRedisTemplate.delete("lockKey")
}
问题2:机器宕机

锁,加过期时间

// 不要这样分开执行,因为宕机无时不在
stringRedisTemplate.expire("lockKey", 10, TimeUnit.SECONDS);

// 使用元执行
 stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "aaa", 10, TimeUnit.SECONDS);
问题3:锁一直失效,乱套

线程运行时间无法预料,会出现下面的问题
逻辑执行一半,过期时间到时,第二次请求又会进去,但是第一次把第二次的锁删了,第三次请求又会进去,第二次又将锁删除
问题根本点:自己的锁,被别的请求删除
解决:每一个锁的值用唯一值及进行标识,删除时做判断

// 设置锁,值为唯一值
String clientId = UUID.randomUUID().toString();
stringRedisTemplate.opsForValue().setIfAbsent("lockKey", clientId, 10, TimeUnit.SECONDS);

// 删除锁,进行值判断
if(clientId.equals(stringRedisTemplate.opsForValue().get("lockKey"))){
	// 这儿又可能出现锁到时问题,也会有并发问题
	stringRedisTemplate.delete("lockKey")
}
锁续命

当一个请求过来,加锁成功,开始执行代码逻辑;
这时,在后台开启一个分线程,进行一个定时任务,定时给锁续命;
检查主线程锁是否被删掉,如果删掉了说明任务执行完成了,否则锁快到过期时间时,去延长锁的超时时间

redisson

jedis儿子
redisson github地址
引入依赖

<dependency>
	<groupId>org.redisson</groupId>
	<artifacId>redisson</artifacId>
	<version>3.6.5</version>
</dependency>

配置

@Bean
public Redisson redisson(){
	// 此为单机模式
	Config config = new Config();
	config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(8);
	return (Redisson) Redisson.create(config);
}

使用

@Autoried
private Redisson redisson;

// 获取一把锁对象
RLock redissonLock = redisson.getLock("lockKey");
// 加锁
redissonLock.lock();

try{
	// 执行业务逻辑
}finally{
	// 删锁
	redissonLock.unlock();
}

在这里插入图片描述

分布式丢锁问题

主从、分布式的情况下锁丢失

上面的实现,单机没问题。但是多机,在主从、分布式的情况下,会有问题
方案一:
zookeeper,主从节点,主leader,从flowwer,遵循CAP,满足一致性
存到主节点,还要同步到子节点,才能生效

redis遵循AP,满足可用性
存则立马生效

redis性能要比zookeeper强很多,但是zookeeper基本不会丢锁

方案二:
Redlock(红锁)
同zookeeper差不多,也需要将加锁key存到多个节点,只有超过半数节点返回添加成功,加锁才成功
这样,新的加锁请求如果存在问题,则不会超过半数,则不会加锁成功

尽量自己用一套分布式锁服务
在这里插入图片描述

红锁问题

问题1:高可用问题
不要通过主从节点去处理,同步的时候就可能有问题。新加锁,因为有子节点,所以半数的计算还是会有多并发问题

高可用,可以多加几个节点,防止节点被挂问题,一般用3-5个节点,注意用奇数节点,别用偶数

问题2:持久化问题
aof持久化方案,1s中一次,依然会丢锁
如果第二个节点不够1s,但是节点挂了,但是重启数据就会掉了,后续的就可以再在这个节点加锁
aof,百分百不丢锁不可能

如果用aways,每一条命令持久化一次,但是性能又太差了

分布式锁性能优化

在redis中是将并行转串行实现,但是特别高的并发,性能较低

锁的粒度越小越好

让串行执行的数量越小越好

分段锁

ConcurrentHashMap底层实现
在redis中分段存储
P_101 = 200
转化为
P_101_1 = 20

P_101_10 = 20
每一个段位都加一个锁
通过分发算法,去请求不同的redis,可以去进行多次并发打到不同请求

这样有多少个段位,就相当于提升了多少倍

电商可能存在问题(下单链路)

订单重复生成

快速多次点击提交订单
多个tab(浏览器)点击提交订单

通过分布式锁处理,key设为(userId + 购物车商品id排序(id1+id2+id2+…))

付钱了,后台状态取消了

马上过期的时候支付,支付成功了,但是后台取消了

通过分布式锁处理,支付的时候加一把锁(针对订单id),取消的时候加一把锁(针对订单id),key一样,则不会执行业务。

Future(成功监听处理)

Lua脚本(redisson内部加锁实现)

在Redis中执行,具备原子性

Apache JMeter工具(压测)

JAVA开发的压测软件,模拟多并发

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python实现元旦多种炫酷高级倒计时_附源码【第20篇—python过元旦】

文章目录 &#x1f30d;python实现元旦倒计时 — 初级(控制台)⛅实现效果&#x1f30b;实现源码&#x1f31c;源码讲解 &#x1f30d;python实现元旦倒计时 — 中级(精美动态图)⛅实现效果&#x1f30b;实现源码&#x1f31c;源码讲解 &#x1f30d;python实现元旦倒计时 — 高…

C++使用HTTP库和框架轻松发送HTTP请求

编程中使用 HTTP 库或框架发送 HTTP 请求 一、引言二、使用Curl库发送HTTP请求三、使用Boost.Beast库发送HTTP请求四、使用cpp-httplib库发送HTTP请求五、自己实现socket发送 HTTP 请求总结 一、引言 使用C编程发送HTTP请求通常需要使用第三方的HTTP库或框架。在C中&#xff0…

57 代码审计-JAVA项目框架类漏洞分析报告

目录 过滤器及拦截器相关区别解释Struts2-016远程代码执行漏洞分析-黑盒流程SpringBoot-SpEL表达式注入漏洞分析-白盒思路 过滤器及拦截器相关区别解释 Filter是基于函数回调的&#xff0c;而Interceptor则是基于Java反射的。 Filter依赖于Servlet容器&#xff0c;而Intercept…

gitattributes配置文件的作用

0 Preface/Foreword Git版本管控工具功能强大&#xff0c;在使用过程中&#xff0c;在多人合作的项目开发过程中&#xff0c;经常会遇到提交代码时出现的warning提醒&#xff0c;尤其是换行符。 Linux/Unix/Mac OS操作系统的换行符使用LF符号&#xff08;\n&#xff09;&…

Dash中 基本的 callback 5

app.callback 在Dash中&#xff0c;app.callback 被用于创建交互性应用程序&#xff0c;它用于定义一个回调函数&#xff0c;该函数在应用程序中发生特定事件时被触发。回调函数可以修改应用程序的布局或更新图表等内容&#xff0c;从而实现动态交互。 下面是一个简单的 app.…

LaTex详细安装及配置(Windows)

文章目录 引言LaTeX简介优势与应用领域 安装环境安装texlive下载texlive安装 编辑器安装texstudio下载texstudio安装 环境配置 使用第一个LaTex文档新建文件编程查看 效果 结语 引言 在当今信息技术高度发达的时代&#xff0c;文档的编辑和排版是我们日常工作和学习中不可或缺…

JavaScript中的prototype和_proto_的关系是什么

JavaScript中的prototype和_proto_的关系是什么 __proto__ 是 JavaScript 中对象的一个内部属性&#xff0c;它指向该对象的原型。JavaScript 中每个对象都有一个 __proto__ 属性&#xff0c;通过它可以访问对象的原型。prototype 是函数对象特有的属性&#xff0c;每个函数都…

蓝桥杯 1223 第 2 场 小白入门赛

蓝桥小课堂-平方和 模拟 1 2 2 2 3 2 ⋯ n 2 n ⋅ ( n 1 ) ⋅ ( 2 n 1 ) 6 1^22^23^2\cdotsn^2\dfrac{n\;\cdot\;(n 1)\;\cdot\;(2n1)}{6} 122232⋯n26n⋅(n1)⋅(2n1)​。 write(n * (n 1) * (n * 2 1) / 6);房顶漏水啦 m a x ( 最大的行 − 最小的行 , 最大的列 −…

DevC++ 用C语言的多线程 实现简单的客户端和服务器

知识来源一&#xff1a; 使用Dev-C实现简单的客户端和服务器-CSDN博客 此先生的博客使用的是win32 SDK来创建多线程&#xff0c;然后鄙人对这个版本的多线程细节不明。于是又重新用C语言的线程替代win32API,以此继续学习服务器代码。 知识来源二&#xff1a;DevC 多线程创建…

[Netty实践] 简单WebSocket服务实现

目录 一、介绍 二、依赖导入 三、基础类准备 四、Handler实现 五、WebSocketChannelInitializer实现 六、WebSocketServer实现 七、前端实现 八、测试 九、参考链接 一、介绍 关于WebSocket此处不进行过多介绍&#xff0c;本章主要着重通过Netty实现WebSocket通信服务…

在线客服系统:解决常见问题的实用工具与解决方案

市场得不断发展促使着消费者服务意识的觉醒&#xff0c;越来越多的消费者在购买产品的时候不仅看产品的功能、外观、性能&#xff0c;还关注品牌的服务质量。在线客服系统的出现帮助企业解决了客户服务难的问题。接下来&#xff0c;我们具体聊一聊在线客服系统能解决哪些问题&a…

每日一题——LeetCode888

方法一 个人方法&#xff1a; 交换后要达到相同的数量&#xff0c;那么意味着这个相同的数量就是两个人总数的平均值&#xff0c;假设A总共有4个&#xff0c;B总共有8个&#xff0c;那么最后两个人都要达到6个&#xff0c;如果A的第一盒糖果只有1个&#xff0c;那么B就要给出6…

铁山靠之——HarmonyOS基础 - 1.0

HarmonyOS学习第一章 一、HarmonyOS简介1.1 安装和使用DevEco Studio1.2 环境配置1.3 项目创建1.4 运行程序1.5 基本工程目录1.5.1 工程级目录1.5.2 模块级目录1.5.3 app.json51.5.4 module.json51.5.5 main_pages.json 二、TypeScript快速入门2.1 简介2.2 基础类型2.2.1 布尔值…

通过 Nginx 代理实现网页内容替换

突发奇想&#xff0c;用 Nginx 代理一个网站&#xff0c;把网站的一些关键字替换掉&#xff0c;蛮有意思的。 如下图&#xff1a; 一、编译安装 Nginx 一般 Nginx 中不包含 subs_filter 文本替换的模块&#xff0c;需要自己手动编译安装&#xff0c;步骤如下。 克隆 subs_fi…

linux cpu调度分析

一、cpu调度调试方法 echo 0 > /sys/kernel/debug/tracing/tracing_on echo > /sys/kernel/debug/tracing/trace echo 30720 > /sys/kernel/debug/tracing/buffer_size_kb echo nop > /sys/kernel/debug/tracing/current_tracer echo sched_switch sched_wakeup s…

移除石子使总数最小(LeetCode日记)

LeetCode-1962-移除石子使总数最小 题目信息: 给你一个整数数组 p i l e s piles piles &#xff0c;数组 下标从 0 0 0 开始 &#xff0c;其中 p i l e s [ i ] piles[i] piles[i] 表示第 i i i 堆石子中的石子数量。另给你一个整数 k k k &#xff0c;请你执行下述操作…

Win11右键菜单显示全部的方法

Win11右键菜单显示全部的方法&#xff1a;1. 用鼠标右键点击“开始”按钮&#xff08;或者按WinX键&#xff09;&#xff0c;选择点击 “Windows 终端&#xff08;管理员&#xff09;”。 2.在终端应用程序里粘贴这串代码【reg.exe add “HKCU\Software\Classes\CLSID{86ca1aa…

实现一个最简单的内核

更好的阅读体验&#xff0c;请点击 YinKai s Blog | 实现一个最简单的内核。 ​ 这篇文章带大家实现一个最简单的操作系统内核—— Hello OS。 PC 机的引导流程 ​ 我们这里将借助 Ubuntu Linux 操纵系统上的 GRUB 引导程序来引导我们的 Hello OS。 ​ 首先我们得了解一下&a…

burpsuite与sqlmap联动(sqlipy配置)

首先我们需要在burpsuite的 扩展-选项 里配置两个路径&#xff1a; 第一个路径为 jython-standalone-2.7.3.jar 的路径 这个jar文件我们需要自己下载&#xff0c;下载地址&#xff1a;https://www.jython.org/ 点击 download 点击 Jython Standalone 下载好之后将这个jar文件…

Django之DRF框架三,序列化组件

一、序列化类的常用字段和字段参数 常用字段 字段名字段参数CharFieldmax_lengthNone, min_lengthNone, allow_blankFalse, trim_whitespaceTrueIntegerFieldmax_valueNone, min_valueNoneFloatFieldmax_valueNone, min_valueNoneBooleanFieldNullBooleanFieldFloatFieldmax_…