Redis缓存问题与缓存更新机制

news2024/11/25 2:03:49

目录

​编辑

 一、缓存问题

 1.1 缓存穿透

 1.1.1 问题来源

 1.1.2 解决方案

 1.1.2.1 缓存空对象

 1.1.2.2 使用布隆过滤器

 1.2 缓存击穿

 1.2.1 问题来源

 1.2.2 解决方案

 1.2.2.1 设置热点数据永远不过期

 1.2.2.2 新增后台定时更新缓存线程(逻辑不过期)

 1.2.2.3 使用分布式互斥锁

 1.2.2.4 接口限流与熔断,降级

 1.3 缓存雪崩

 1.3.1 问题来源

 1.3.2 解决方案

 1.3.2.1 缓存过期时间随机

 1.3.2.2 分布式部署

 1.3.2.3 设置热点数据永远不过期

 1.3.2.4 接口限流与熔断,降级

 二、缓存更新机制

2.1 缓存更新策略分类

 2.2 内存淘汰机制

 2.2.1 noeviction

 2.2.2 volatile-lru

 2.2.3 volatile-lfu

 2.2.4 volatile-ttl

 2.2.5 volatile-random

 2.2.6 allkey-lru

 2.2.7 allkey-lfu

 2.2.8 allkey-random

 2.3 超时剔除

 2.3.1 定时删除

 2.3.2 惰性删除

 2.4 主动更新

 2.4.1 主动更新策略

 2.4.1.1 Cache Aside Pattern

 2.4.1.2 Read/Write Through Pattern

 2.4.1.3 Write Behind Caching Pattern

 2.4.2 主动更新策略需要考虑的三个问题

 2.4.1 删除缓存还是更新缓存?

 2.4.1.1 删除缓存

 2.4.1.2 更新缓存

 2.4.2 如何保证缓存与数据库的操作同时成功或失败?

 2.4.3 先操作缓存还是数据库?

 2.4.3.1 先删除缓存,再操作数据库

2.4.3.2 先操作数据库,再删除缓存

2.4.3.3 延时双删策略

 2.5 缓存更新机制总结


 一、缓存问题

 1.1 缓存穿透

 1.1.1 问题来源

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

 1.1.2 解决方案

 1.1.2.1 缓存空对象

从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。

 1.1.2.2 使用布隆过滤器

类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小。

 1.2 缓存击穿

 1.2.1 问题来源

缓存击穿是指缓存某些热点数据失效(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

 1.2.2 解决方案

 1.2.2.1 设置热点数据永远不过期

可以在刷缓存时,设置热点数据不过期。

 1.2.2.2 新增后台定时更新缓存线程(逻辑不过期)

后台新增一个缓存更新线程,缓存快要过期前刷新缓存时间,防止缓存失效。

 1.2.2.3 使用分布式互斥锁

可以使用Redis提供的分布式互斥锁,保证只有一个请求查询数据库和更新缓存,其他请求阻塞等待缓存更新完成后在访问缓存。

 1.2.2.4 接口限流与熔断,降级

重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。

 1.3 缓存雪崩

 1.3.1 问题来源

缓存雪崩是指Redis缓存不能正常提供服务了(阻塞、服务宕机、大面积缓存失效等造成),导致所有请求都落到了数据库上,增加了数据库压力或者导致数据库宕机。

 1.3.2 解决方案

 1.3.2.1 缓存过期时间随机

缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

 1.3.2.2 分布式部署

采用分布式部署方式部署缓存,避免缓存服务单节点,同时将热点数据均匀分布在不同的缓存数据库中。

 1.3.2.3 设置热点数据永远不过期

可以在刷缓存时,设置热点数据不过期。

 1.3.2.4 接口限流与熔断,降级

重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。

 二、缓存更新机制

2.1 缓存更新策略分类

内存淘汰超时剔除主动更新
说明重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。给缓存数据添加TTL时间,到期后自动删除缓存,下次查询时更新缓存编写业务逻辑,在修改数据的同时,更新缓存
一致性一般
维护成本

 2.2 内存淘汰机制

 2.2.1 noeviction

不淘汰,这是默认的淘汰策略;当内存达到限制后,写请求(set)会返回错误,读请求(get)和删除请求(del)可以继续进行

 2.2.2 volatile-lru

内存不足时,在设置了过期时间的key中,优先删除最近最少使用的key

 2.2.3 volatile-lfu

内存不足时,在设置了过期时间的key中,优先删除使用频率最少的key

 2.2.4 volatile-ttl

内存不足时,在设置了过期时间的key中,优先删除存活剩余时间最少的key

 2.2.5 volatile-random

内存不足时,在设置了过期时间的key中,随机删除某个key

 2.2.6 allkey-lru

内存不足时,在全体key范围内,优先删除最近最少使用的key

 2.2.7 allkey-lfu

内存不足时,在全体key范围内,优先删除使用频率最少的key

 2.2.8 allkey-random

内存不足时,在全体key范围内,随机删除某个key

 2.3 超时剔除

 2.3.1 定时删除

设置一个定时任务,随机抽取部分过期时间的key,检查是否过期,过期了就清除掉

 2.3.2 惰性删除

查询获取数据时,检查缓存是否过期,过期则删除,没过期不删除

Redis 默认采用惰性删除+定时删除结合的过期策略

 2.4 主动更新

 2.4.1 主动更新策略

 2.4.1.1 Cache Aside Pattern

由缓存的调用者,在更新数据库的同时更新缓存。

 2.4.1.2 Read/Write Through Pattern

缓存和数据库整合为一个服务,由服务来维护一致性。调用者调用服务,不用关心一致性问题。

 2.4.1.3 Write Behind Caching Pattern

调用者只操作缓存,由其他线程异步的将缓存数据持久化到数据库,最终保持一致。

在企业中使用最多的主动更新策略是 Cache Aside Pattern。也就是我们自己编码来保证数据的一致性。

 2.4.2 主动更新策略需要考虑的三个问题

 2.4.1 删除缓存还是更新缓存?

 2.4.1.1 删除缓存

更新数据库时让缓存失效,查询时再更新缓存。(延迟加载)一般选择这个方案。

这个方案比较合理一点,可以避免过多的无效写操作,缓存删除后,只要没人来查询这条数据,数据就不会被写入缓存,这样就可以避免大量无效的写操作

 2.4.1.2 更新缓存

每次更新数据库都更新缓存,无效写操作比较多。

这种方式的缺点很明显,举个例子:假如我更新了100次数据库,然后又同时更新了100次缓存,但是在更新的时候并没有人来查这个数据,那么我更新这100次缓存好像也没啥用吧,相当于前99次都是无用功,只有最后一次才是有用的。这就是无效写操作过多的原因。

 2.4.2 如何保证缓存与数据库的操作同时成功或失败?

1)单体系统,将缓存与数据库操作放在一个事务中。

2)分布式系统,利用TCC等分布式事务方案。

 2.4.3 先操作缓存还是数据库?

 2.4.3.1 先删除缓存,再操作数据库

这种方式存在很明显的问题,假设有两个并发操作,线程A更新,线程B查询。线程A先删除缓存,然后还没来得及更新数据库,CPU资源被线程B抢走,线程B查询缓存发现没有命中(因为已经被线程A删除了),查询数据库,然后把结果写入到缓存中。这个时候线程A终于抢到CPU资源了,然后更新数据库,此时就会造成数据不一致问题。

2.4.3.2 先操作数据库,再删除缓存

这种处理方式使用的频率是最高的,因为出错的概率非常小,只有一种比较极端的情况才会出现数据一致性问题。

同样有两个并发请求,线程A查询、线程B更新,当线程A查询的时候,缓存刚好失效,然后就去查询数据库拿到数据,在准备写入缓存的时候,CPU资源被线程B抢走,线程B开始更新数据库,然后删除缓存(这一步其实等于无用,因为缓存已经过期)。此时线程A再次获取到CPU资源,然后写入缓存,此时写入的是更新前的旧数据,会产生数据一致性问题。

看起来这确实也是一个问题,但是我们仔细分析一下这种情况都需要满足哪些条件:

1)并发读写操作
2)读缓存时,缓存刚好失效
3)写数据库操作要比写缓存快

写数据库是操作磁盘,写缓存是操作内存的,所以不太可能会出现写磁盘的速度快于写内存的。因此使用这种方式出现数据一致性的概率是很小的。

2.4.3.3 延时双删策略

 

延迟双删策略是分布式系统中数据库存储和缓存数据保持一致性的常用策略,但它不是强一致。其实不管哪种方案,都避免不了Redis存在脏数据的问题,只能减轻这个问题,要想彻底解决,得要用到同步锁和对应的业务逻辑层面解决。

前面两种方案的不足点我们进行了分析,第二种方式的使用频率比较高,但是也有一些小缺陷,虽然说发生的概率很低,但是这个概率到了线上会不会发生也不好说,所以就有了延时双删策略对第二种方式做补充。

所谓延时双删就是先进行缓存清除,再执行数据库操作,最后(延迟N秒)再执行缓存清除。延迟N秒的时间要大于一次写操作的时间,这个延时N秒就是了完善保证第二种策略中不足,可以保证线程A的写缓存和线程B的修改数据库、删除缓存都执行完毕,然后再删除缓存一次,就可以保证后面再来的查询请求可以查询到最新数据。

ps: 一般的延时时间设置为3S左右,具体情况要根据业务场景取最佳值。

 2.5 缓存更新机制总结

1)内存淘汰:不用自己维护,利用Redis内存淘汰机制,自动删除部分缓存数据,这些被删除的数据在下一次被查询时更新。这种方式一致性最差。

2)超时剔除:给缓存数据加上过期时间 ,到期后自动删除,下次查询时更新,数据一致性问题大概率会出现。维护成本比较低。

3)主动更新:编写业务逻辑,在修改数据库的同时更新缓存,一致性比较好,维护成本比较高。一般采用先操作数据库再更新缓存的方式。

一般在数据一致性要求比较低的场景下可以使用内存淘汰机制,比如商城首页的分类信息,这些东西基本上是不会变化的。如果一致性要求比较高,我们可以采用主动更新+超时剔除兜底的方式来处理。

如果觉得对您有帮助,欢迎点赞+收藏+关注!

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

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

相关文章

详解Java Synchronized锁升级原理

✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。 🍎个人主页:Hhzzy99 🍊个人信条:坚持就是胜利! 💞当前专栏:JAVA多线程 🥭本文内…

蓝桥杯专题-试题版含答案-【猴子吃桃问题】【小光棍数】【九九乘法表】【谁是最好的Coder】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 👉关于作者 专注于Android/Unity和各种游…

(Linux) 套接字socket基础

文章目录 前言基本原理 Codeserverclient 核心函数socketbindlistenacceptrecvsendconnectclose 多线程改进END 前言 本文将以纯C语言描述,编译器gcc。 C/C没有标准的网络库,因为都需要用到各个平台的接口才行。 本文讲解Linux下最基础的socket编程&a…

uniapp 常用提示弹框整理

一. 加载提示弹框 在执行数据查询、页面数据渲染等过程中弹出提示。以页面渲染为例: //前端数据请求时,显示加载提示弹框 uni.showLoading({title: 加载中... }); // 数据从后端接口返回后,提示弹框关闭 uni.hideLoading();效果如下&#x…

Sui Builder House京都站|创意大赛获奖名单公布

Sui Builder House京都站于6月30日(周五)圆满结束,这是一次Sui生态系统项目演示和展示各自产品的良好机会。构建者们向大家展示了游戏、NFT、DeFi和基础设施赛道的项目,同时现场演讲还介绍了Sui的最新进展以及有关AI和用户体验设计…

React环境安装配置

React环境安装配置 一、前提二、React安装 一、前提 安装本地React环境需要Node.js,如果具有Node环境跳过即可。如果没有安装则可参考该篇文章安装Node环境,点击查看 二、React安装 全局安装React 首先打开命令行,建议以管理员身份输入命…

20230704测试STC32G实验箱9.6(STC32G12K128)开发板的虚拟串口(C语言深入了解)

20230704测试STC32G实验箱9.6(STC32G12K128)开发板的虚拟串口(C语言深入了解) 06第五集:C语言运算符和进制数入门上.mp4 07第五集:C语言运算符和进制数入门下.mp4 2023/7/4 19:00 下次 在【冲哥】录视频的时…

024、数据库管理之数据同步工具TiCDC

TiCDC TiCDCTiCDC介绍架构与原理适用场景对已有TiDB进行扩容部署TiCDC管理工具TiCDC同步任务查询所有TiCDC同步任务查询TiCDC指定的同步任务管理TiCDC同步任务动态更新任务监控TiCDC 实验数据同步完整实操缩容当前TiCDC节点 TiCDC TiCDC介绍 TiCDC 是一款通过拉取 TiKV 变更日…

leetcode 42-接雨水

解法一:双指针(暴力按列计算) 首先,如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。 可以看出每一列雨水的高度,取决于该列左侧最高的柱子和右侧最高的柱子中…

VMware虚拟机的基本操作:快照和克隆

VMware虚拟机的基本操作 一、虚拟机快照二、克隆 一、虚拟机快照 “快照”是虚拟机磁盘文件(VMDK)在某个点及时的副本。系统崩溃或系统异常,你可以通过使用恢复到快照来保持磁盘文件系统和系统存储。当升级应用和服务器及给它们打补丁的时候…

一桩关于Json序列化引发的惨案(Go系统)

文章目录 前言突然崩了排查问题关于go的json库什么是反射 解决大结构体序列化的性能问题干掉大结构体减少反射使用一些好用的第三方序列化包 自定义序列化 写在最后 前言 一个风和日丽的下午,线上系统突然开始报警(系统温馨提示,您的服务接口…

部署Git服务器

哈喽,大家好,本次为大家演示如何部署git服务器. 首先要准备gitea和phpstudy_pro phpstudy一路nest即可,可以帮你安装mysql和阿帕奇。 登录127.0.0.1:3000注册gitea即可。 如何在上传公钥的时候出现500的错误,加入这句便可解决…

SpringBoot+Vue+Element-ui实现文件下载

目录 1.后端代码部分 2.前端代码部分 3.效果展示 1.后端代码部分 GetMapping("downloadFile")public void downloadFile(RequestParam("filename") String filename,HttpServletResponse response) throws Exception {// 告知浏览器这是一个字节流&…

WebSocket connection to “wss://xxx/xxx“ failed

用了https后,需要用nginx作websocket地址的转发才能使用wss,别直接用端口访问 location /ws/ {proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade"; proxy_pass http://localhost:10001/…

JVM源码剖析之SymbolTable和StringTable

很多读者在观看JVM相关的书籍时会看到SymbolTable和StringTable,书中的三言二语介绍的不是很清楚,并且读者的水平有限,导致无法理解SymbolTable和StringTable。所以特意写此篇图文并茂的文章来彻底理解SymbolTable和StringTable这两张表。 版…

Java面试Day16

1.Dubbo 是什么?是否了解过它的架构设计? Dubbo官网:Apache Dubbo Dubbo是一个高性能、轻量级的开源Java RPC框架,它提供了完整的RPC协议栈,包括服务发布、服务引用、负载均衡、容错、服务治理和服务监控等功能&#…

构造函数使用初始化列表+模板

构造函数使用初始化列表模板 注意对应关系&#xff1a; Stack(int size) ;template<class DataType> inline Stack<DataType>::Stack(int size) : stacksize(size), top(0) {item new DataType[stacksize];if (item ! nullptr) cout << "成功初始化栈&…

再见!Fastjson!

你为何仍用Fastjson&#xff1f; 原因可以说出5678种&#xff0c;总而言之言而总之&#xff0c;你不&#xff08;敢&#xff09;切换的原因或许只有一个&#xff1a;Fastjson的静态方法调用用着省心&#xff1b;最重要的是&#xff0c;对其它JSON库&#xff08;如Jackson/Gson…

【生活分享】《哈姆雷特》· 苏州站 - 一次有趣的音乐剧体验

平时博客里大部分都是写技术相关的文章&#xff0c;感觉内容还是比较单一的&#xff0c;也想分享一下最近的一些有趣的生活~ 6月30号的时候&#xff0c;便订好了徐俊导演编排的7月2号场《哈姆雷特》音乐剧。而实际上&#xff0c;苏州场7月1号就上映了&#xff0c;感觉那天太晚……

C++图形开发(6):落下后能弹起的小球

文章目录 1.重复下落的小球2.落下后能弹起的小球3.能上下反弹的小球4.符合重力的能上下反弹的小球 今天我们来尝试实现一个落地后可以弹起的小球 1.重复下落的小球 首先&#xff0c;我们要来实现一个小球的重复下落 我们知道&#xff0c;在前面的代码中&#xff08;详见C图形…