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

【Redis】缓存和分布式锁

目录

一、缓存

1、缓存使用

高并发下缓存失效问题 - 缓存穿透(查询一个不存在的数据,缓存与数据库中都没有)

高并发下缓存失效问题 - 缓存雪崩(大面积数据 key 同时失效,过期时间相同)

高并发下缓存失效问题 - 缓存击穿(高并发访问一个刚好失效的热点 key)

二、分布式锁

一、什么是分布式锁

二、Redis分布式锁原理

三、引入过期时间

四、引入校验id

五、引入Lua

六、引入看门狗

七、引入RedLock算法

一、缓存

1、缓存使用

为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访问。而 db 承担数据落盘工作(持久化工作),数据库查询一次后将数据存入缓存,以后需要该数据直接从缓存中取。

适合放入缓存的数据:

  • 即时性、数据一致性要求不高的
  • 访问量大且更新频率不高的数据(读多,写少

缓存中存放的所有对象都应该是 JSON 字符串,JSON 跨语言、跨平台兼容。

给缓存中存放 JSON 字符串,从缓存中拿出的 JSON 字符串,还要逆转为能用的对象类型【序列化与反序列化的过程】

高并发下缓存失效问题 - 缓存穿透(查询一个不存在的数据,缓存与数据库中都没有)

缓存穿透:

指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也无此记录,我们没有将这次查询的 null 写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义

风险:

利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃

解决:

null 结果缓存,并加入短暂过期时间

高并发下缓存失效问题 - 缓存雪崩(大面积数据 key 同时失效,过期时间相同)

缓存雪崩:

指在我们设置缓存时 key 采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到 DB,DB 瞬时压力过重导致雪崩

解决:

原有的失效时间基础上增加一个随机值,比如 1~5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

高并发下缓存失效问题 - 缓存击穿(高并发访问一个刚好失效的热点 key)

缓存击穿:

  • 对于一些设置了过期时间的 key,如果这些 key 可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据
  • 如果这个 key 在大量请求同时进来前正好失效,那么所有对这个 key 的数据查询都落到 DB,这就是缓存击穿

解决:

加锁 —— 大量并发只让一个人去查,其它人等待,查到以后释放锁,其他人获取到锁,先查缓存,就会有数据,不用再去查 DB

二、分布式锁

一、什么是分布式锁

分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。

一把靠谱的分布式锁应该具有以下特征:

二、Redis分布式锁原理

锁的实现主要基于redis 的SETNX(SET if Not eXists 如果不存在,则 SET)命令

SETNX key value将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。【设置成功,返回 1; 设置失败,返回 0】。

使用SETNX完成同步锁的流程及事项如下:

  1. 使用SETNX命令获取锁,若返回0(key已存在,锁已存在)则获取失败,反之获取成功

  2. 为了防止获取锁后程序出现异常,导致其他线程/进程调用SETNX命令总是返回0而进入死锁状态,需要为该key设置一个“合理”的过期时间

  3. 释放锁,使用DEL命令将锁数据删除

三、引入过期时间

        当服务器1加锁之后,开始处理的过程中,如果服务器1意外宕机了,就会导致解锁操作(删除该 key)不能执⾏.就可能引起其他服务器始终⽆法获取到锁的情况. 为了解决这个问题,可以在设置key的同时引⼊过期时间.即这个锁最多持有多久,就应该被释放.

可以使用set ex nx在设计锁的同时设置过期时间

四、引入校验id

在 Redis 中写入的加锁键值对,其他节点也可以删除。比如服务器 1 写入一个 “001”: 1 这样的键值对,服务器 2 是完全可以把 “001” 给删除掉的。当然,服务器 2 不会进行这样的 “恶意删除” 操作,不过不能保证因为一些 bug 导致服务器 2 把锁误删除。为了解决上述问题,我们可以引入一个校验 id。比如可以把设置的键值对的值,不再是简单的设为一个 1,而是设成服务器的编号,形如 “001”: “服务器 1”。这样就可以在删除 key(解锁)的时候,先校验当前删除 key 的服务器是否是当初加锁的服务器,如果是,才能真正删除;不是,则不能删除。

String key = [要加锁的资源 id];
String serverId = [服务器的编号];
// 加锁, 设置过期时间为 10s
redis.set(key, serverId, "NX", "EX", "10s");
// 执⾏各种业务逻辑, ⽐如修改数据库数据. 
doSomeThing();
// 解锁, 删除 key. 但是删除前要检验下 serverId 是否匹配. 
if (redis.get(key) == serverId) {redis.del(key);
}

五、引入Lua

为了使解锁操作原子,可以使用Redis的lua脚本功能

if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) 
else  return 0 
end;

上述代码可以编写成一个.Lua后缀的文件,由redis-cli等客户端加载并且发送Redis服务器,由Redis服务器执行这个逻辑,一个Lua脚本会被Redis服务器以原子的方式执行

六、引入看门狗

上述的方案仍然存在一个重要的问题,当我们设置了key过期时间后,有一定的可能性,当任务没有执行完,key就先过期了,这就导致锁提前失效

所谓的watch dog,本质上是加锁的服务器的一个单独的线程,通过线程来对锁的过期时间来进行续约

举个例子:

初始情况下设置的过期时间为10s,同时设定看门狗线程每隔3s检测一次

那么当3s时间到的时候,看门狗就会判定当前任务是否完成

如果已经完成,则直接通过lua脚本的方式释放锁

如果任务未完成,则把过期时间重写设置为10s

七、引入RedLock算法

实践中的Redis一般是以集群的方式部署的

服务器1向master节点进行加锁的操作,这个写入key的过程刚刚完成,master挂了;slave节点升级成了新的master节点,但是由于刚才写入的key尚未来得及同步给slave,此时就相当于服务器1假的锁形同虚设了,服务器2仍然可以进行加锁

为了解决这个问题,引入了RedLock算法

我们引入一组Redis节点,其中每一组Redis节点都包含一个主节点和若干个从节点,并且存储的数据都是一致的,相互之间是备份关系,加锁的时候按照一定的顺序,写入多个master节点,设定超时时间

简而言之,RedLock算法核心是加锁操作不能只写给一个Redis节点,而要写多个

http://www.dtcms.com/a/345021.html

相关文章:

  • MySQL - 视图,事务和索引
  • AAA 服务器与 RADIUS 协议笔记
  • C语言初学笔记【联合与枚举】
  • Unreal Engine USceneComponent
  • 如何实现二维CAD与3D建模工程图关联一体化出图 | 中望3D 2026新亮点
  • android sdk 虚拟机是否可以通过命令行打开?
  • 数字逻辑与数字系统设计之电梯控制器设计
  • 防爆连接器在防爆箱上的作用
  • shell脚本第二阶段-----选择结构
  • Unreal Engine IWYU Include What You Use
  • DLT645仪表通信,串口助手调试读写地址
  • 【C#】观察者模式 + UI 线程调度、委托讲解
  • vuex如何在js文件中使用
  • NVIDIA GB200 架构详解及与 B200/H200/H100 的区别
  • 【芯芯相印】芯片设计生产全流程核心技术术语与实践指南:从架构定义到量产交付的完整图谱
  • NLP学习之Transformer(2)
  • 数据预处理学习笔记
  • Thunderbird 将推出在德国托管的加密电子邮件服务
  • Android Jetpack | Hilt
  • 快速了解深度学习
  • 数学建模--Topsis(Python)
  • 学习python第12天
  • 第5.3节:awk数据类型
  • gcc 和 make 命令
  • 机试备考笔记 17/31
  • 打工人日报20250822
  • Redis 部署模式深度对比与选型指南
  • 计算机毕设大数据方向:电信客户流失数据分析系统技术实现详解
  • ​如何用 Windows 10 ISO 文件重装系统?U盘安装教程(附安装包下载)
  • Kubernetes 调度器 详解