redis分布式锁的应用

本文介绍了如何使用Redis实现分布式锁,特别是通过lua脚本提高性能和原子性。讨论了并发问题、lua脚本续命机制、非公平锁特性以及在高并发场景下的多级缓存策略,包括数据冷热分离和热点数据探测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis 作为分布式锁的东西 分布式锁的应用
redis,zk,数据库这些都可以实现分布式锁
我们今天主要基于redis实现的分布式锁,而且要求性能要好
基于一个小的业务场景来说,就比如说秒杀中的减库存,防止超卖

在这里插入图片描述

这种代码就会有并发问题,比方说3个线程  同时查出来 之后会set 299  此时就超卖了
这就是我们典型的超卖问题,我们可以加锁,比如说我们常见的sy,JVM进程级别的锁

在这里插入图片描述

这样就可以解决我们的问题,如果说我们此时上线部署之后就一台服务器,也就一个tomcat 
单机环境 是没有问题 假设我们做集群项目的时候 此时就会出现问题
我们的项目基本上都是 基于nginx 进行转发,利用nginx做负载均衡,
在nginx的upstream中配置负载均衡的地址

在这里插入图片描述

一般来说我们都会搞这些集群架构部署,这种场景下我们jvm 级别锁就不可用了
我们可以模拟高并发场景利用jmeter基于高并发下发请求
jmeter--->nginx---->tomcat1/tomcat2
发现如果此时利用jvm级别的锁就会出现问题

在这里插入图片描述

一般情况下这种问题要解决的话 就用分布式锁 分布式锁 可以用redis
我们一般使用redis的setnx来实现分布式锁
基于setnx命令我们就可以实现一个分布式锁,在redis这边 只会有1个请求执行成功
这样我们就写完了

在这里插入图片描述

我们基于这种简单的setnx 实现的分布式锁有什么问题呢
Q1 如果我第一个线程拿到锁执行一半的时候 就会抛异常 抛异常之后 我就不会delete掉了
这个时候就相当于死锁了,key永远在redis中  其他线程要想来执行就会执行不成功

在这里插入图片描述

我写个try catch finally 意思是如果跑异常了 我还可以执行redis.delete 	
如果现在执行一半后 客户端宕机了,或者被运维重启了,呢么此时finally也是不能被执行的,我们可以设置一个超时时间,比方说10s,

在这里插入图片描述

我们可以加个超时时间,意味着如果你业务宕机了,过段时间 redis内部自己就释放锁了
如果并发特别大的情况下.这种代码也会造成超卖,在并发情况下 接口的响应可能会变慢

如果线程一执行了一半,超时时间到了, 这个时候线程2就会进来,然后此时线程一就会吧线程2的这个锁解开
线程1 删除(释放了)了线程2的锁  此时就会出现问题
出这个问题的根本点就在于  我自己加的锁被别人释放了

在这里插入图片描述

我们可以加个uuid就是说 我只能释放我自己加的锁,这个时候的代码就很完善了

在这里插入图片描述

假设一个线程枷锁成功了执行到这行代码的时候,卡顿一会,我们的10s到期了
其他线程又可以基于这个Lock进行加锁,这个时候线程1还是释放了线程2的锁
所以我们要保证这2行代码的原子性

我们的业务没有执行完毕 锁的超时时间就结束了
针对于分布式锁 有个锁续命机制 
分线程给锁进行续命,判断主线程有没有业务执行完毕,如果没有结束就续命
每过10s之后就续命,我的主线程一直执行 我的锁就不会失效,因为他一直被分线程做续命操作
以后我主线程要是把锁释放了,分线程会判断锁还被主线程持有 如果持有 就会做续命操作
如果不持有就不再跑任务了
Redisson  就实现了这种机制 

在这里插入图片描述

lock.lock 就可以获取得到锁了
Lock.unLock 加锁 解锁
https://www.cnblogs.com/xiaoyangabc/p/16906922.html
redis枷锁的核心流程, 假设有2个线程同时都是一个key执行  redis调用他的lock方法
只有1个线程执行成功,假设线程1执行成功。 线程2 执行没有成功
他会whlie循环自璇  尝试加锁
线程1 加锁成功 他会另外开启一个分线程,分线程就会对这个key 进行续命,
这就是整个redis分布式锁续命的核心业务流程

在这里插入图片描述

redis分布式锁底层是基于Lua脚本来写的,
lua脚本,减少网络开销。可以批量执行redis命令,类似于管道可以把一批命令打包发给redis 服务端去执行
比如说原来有4条命令,我就需要发送4次远程交互(网络调用)
但是我用管道或者lua脚本的话 就只需要一次网络调用
对于Lua脚本也一样,假设我有10条redis命令,我也可以放到lua脚本来一次性发给服务端去执行
lua脚本是支持原子操作的,一段Lua脚本,要么同时成功,要么同时失败
lua脚本都执行完毕 之后 其他命令才可以执行(因为redis服务端是单线程来执行任务的)
我这个命令执行的中间是不可以被其他线程插入的
Lua  支持事务在redis中可以执行lua脚本
用lua脚本是个原子操作

在这里插入图片描述
在这里插入图片描述

redis分布式锁的原理,当多个线程进行抢锁时候,只有一个线程来设置成功,其他没有抢到锁的线程就会while自旋尝试加锁,通过lua脚本进行加锁,加锁成功后就会对其进行续命,加锁失败就会自璇,续命锁
(Lua脚本进行续命) 每次续命1/3(判断主线程持有的呢吧锁是否还存在,如果存在我就进行续命,吧主线程的超时时间重新设置为30S ,如果不存在,我就结束)

说白了redis实现分布式锁的核心东西也就完成了,基本上是基于Lua来实现的  借助redis的单线程帮我们实现原子性.来解决并发问题

当我们其他线程如果没有加锁成功,lock 没有加锁成功会怎么办.假设有10个线程while循环枪锁 他的cpu 就会100%,如果说我们redis 没有抢到锁 他自选再去加锁 他是怎么来做的
其他线程会返回锁剩余加锁时间的剩余时间 并且阻塞让出cpu

比如说第一个线程执行了5S之后
假设我我第二个线程会尝试的过来加锁 如果没有加锁成功会再这里等25S  阻塞等待会让出cpu
25s过后之后再尝试加锁while循环间断性的加锁
假设有1000个线程来了同时超时 同时while循环  此时是非公平锁 也就是redis 默认 是非公平锁

在这里插入图片描述

所以 多个线程来抢锁时,底层会基于lua脚本进行加锁,但是只有一个线程来加锁成功,抢锁成功后后台开启一个分线程来对其进行续命,其他线程如果没有抢到锁 会while尝试加锁 但是 并不是死循环
而是会阻塞等待,返回第一次尝试加锁返回的时间,并且此时会让出cpu片段

如果此时业务时间比较快,快速吧这把锁释放了. 呢我其他线程不能一直阻塞 在解锁的方法中有一个唤醒机制 通过redis的发布订阅 他会往Queue发消息
没有加锁成功的线程会去监听Queue,呢么其他线程就会唤醒阻塞  继而继续while循环继续抢锁
 
redis的数据冷热处理
  

在这里插入图片描述

中小型公司使用redis作为缓存,会将数据放入redis中进行缓存,用来提升系统的并发能力

在这里插入图片描述

如果你的系统流量不是特别大 这么用就可以,但是如果并发量大的话  就会有redis 缓存问题
呢么在高并发情况下有什么问题  例如京东
后台商品很多,我的redis 里面会有很多缓存 呢么我的redis容量要很大
呢对于我们redis ,整个线上数据上亿 你丢到redis中对我们redis存储容量是很大的
我们真正的电商网站 真正高频访问的电商  不到1%  大量商品都是冷门商品
我没有必要吧很少去访问的商品也丢到缓存  浪费我们的资源
其实我们真正用缓存的目的是尽可能吧经常访问的 热点缓存 尽量在缓存中多呆一会
冷门数据 不用一直呆在缓存中
所以我们要对redis的数据做冷热分离
1、 我们可以放置redis 的时候加个随机事件   比如说1天, 在get的时候如果在redis中找到的话 
我们继续给他延迟

在这里插入图片描述
在这里插入图片描述

对于不怎么访问的冷门商品在缓存中存储1天就失效了,对于我们一直在访问的商品内存 尽可能的一直呆在缓存中 这样会对我们数据库的请求会大大减少.呢所以在查询缓存做一个读取延期
如果我这么来做的话,对于每天都来访问的商品会一直在我们缓存中.那些不怎么访问的商品可能也就上架完毕就不在缓存中,实际上我就实现了数据的冷热分离,热点数据 我尽可能的留在缓存中,冷门的数据 不会再缓存中,简单方案的冷热数据分离
一般情况下,大规模数据我们要做一些数据的冷热分离 让热数据尽可能丢到缓存,而且要常驻缓存
冷数据可能要查数据库的,让数据库资源就可以给其他模块用了

 
1.为什么要用多级缓存  优点 缺点
2.什么场景下要用到多级缓存
我们一般会使用 缓存架构
  一般公司会使用通过nginx+lua(openresty)+本地缓存+redis这种三级缓存来处理
  如果本地缓存发生改变可以发送mq或者通过zk 可以达到一致性

使用本地缓存 减少读压力
基于本地缓存的缺点
Echache jvm级别缓存  存储热点数据,
Echache 落地 mysql (或者文件中)
数据不一致我们只需要保证他最终一致性就可以了  比如说缓存改变后 发个mq 
或者通过zk来做

在这里插入图片描述
在这里插入图片描述

热点探测服务的原理和实现
我们jvm 级别的数据不支持大数据存储, 所以对于热点数据我们要进行发掘再进行存储

在这里插入图片描述

热点数据就会满足2个条件  有限时间 流量高聚
基于这种有预期的热点 我们可以提前 在这种热点开起之前 我们有很多方式进行优化
比如说对服务进行扩容和降级
基于没有热点的这种场景 我们需要做探测,探测之后放到redis中
呢么没有预期的热点
1.黑客攻击 
2.突然爆火的商品 或者突然大卖的商品 比如说疫情下的口罩

在这里插入图片描述

滑动窗口 对于我们热点数据的探测和发现

在这里插入图片描述

对数据层次的风险  瞬时高并发请求
对应用服务的风险  每个服务的处理是有限的
所以我们需要这么一套热Key的检测机制 来通过对需要检测的热key 进行
发掘
这就是我们使用热点探测的好处,主要是基于滑动时间窗口的计数器来做,比如说我可以设置一个规则
在1分钟内出现10次的商品 我就认为他是热点数据

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值