Redis缓存问题:穿透,击穿,雪崩,双写一致性等

news2025/2/25 5:33:36

Redis缓存问题:穿透,击穿,雪崩,双写一致性等

在高并发场景下,数据库往往是最薄弱的环节,我们通常选择使用redis来进行缓存,以起到缓冲作用,来降低数据库的压力,但是一旦缓存出现问题,也会导致数据库瞬间压力过大甚至崩溃,从而导致整个系统崩溃.今天就聊聊常见的redis缓存问题.

缓存击穿

缓存击穿一般指redis中的一个热点数据过期,导致大量请求直接访问数据库的情况,导致数据库瞬间压力过大甚至崩溃.

解决方案:

  1. 设置热点数据永不过期,这是一个不错的方案(要考虑业务特性,体量以及成本),前提是热点数据不能频繁发生改变,否则就会出现缓存污染.最好是根据一定的策略进行定时更新
  2. 重要接口限流,做好熔断和降级的准备,sentinel是个不错的选择
  3. 使用互斥锁,保证同一时刻只有一个线程可以访问数据库,这何尝不是一种限流呢

缓存穿透

缓存穿透指缓存和数据库中都没有的数据,用户不断发起请求.这种情况最可能就是有人试图恶意攻击系统

解决方案:

  1. 加校验:拦截非法请求,用户鉴权等
  2. redis缓存一个无效值,以防止对同一个key在数据库中的多次查询,但redis中可能会出现大量无效值,导致缓存污染,所以要将有效时间设置得短一些
  3. 添加布隆过滤器,在对数据库进行查询前,先通过布隆过滤器判断是否存在

一般来说这三种方案是同时使用的,第一层一般是校验,拦截部分非法用户和不合理请求(拦截不可能全部拦截而且如果攻击者通过某些方式掌握了大量合法用户呢),第二层是布隆过滤器,尽量避免对数据库的直接访问,但仍然有误判的可能性,第三层再缓存一个无效值,做到尽可能降低风险

缓存雪崩

缓存雪崩一般指reids中大批量数据在极短时间内(同时)过期,导致大量的查询数据库

解决方案:

  1. 在存储数据时,设置过期时间为一个随机值(也可以理解成给固定的过期时间加上一个随机值,类似密码学中的加盐),尽量保证不会有大量数据在同一时间过期
  2. 将热点数据尽量均匀地分布在不同的数据库中
  3. 多级缓存
  4. 设置热点数据永不过期(同缓存击穿中的)

缓存污染

缓存污染指的是缓存中一些只会被访问一次或者几次的的数据,被访问完后,再也不会被访问到,但这部分数据依然留存在缓存中,消耗缓存空间,也会在一定程度上影响redis的性能

redis缓存的maxmemory应该设置多大,这是一个关乎性能和成本的问题,需要根据实际情况进行权衡,但普遍推荐的是设置为总数据量的15%-30%(其他博客都这么写,而且范围还挺大,应该没什么问题🤔)

缓存淘汰策略

官方文档写了8种,如下图:
请添加图片描述

  • noeviction(不驱逐,即不淘汰)

    默认策略,当缓存达到maxmemory时,redis会拒绝所有写请求,并返回错误信息,此时redis已经进入只读模式,无法再进行写操作,但仍然可以进行读操作

  • allkeys-lru
    所有key采用LRU算法进行淘汰,即优先删除最近最少使用的key

  • allkeys-lfu
    所有key采用LFU算法进行淘汰,即优先删除最不常用的key

  • volatile-lru
    只淘汰设置了过期时间的key,采用LRU算法进行淘汰

  • volatile-lfu
    只淘汰设置了过期时间的key,采用LFU算法进行淘汰

  • allkeys-random
    所有key采用随机删除

  • volatile-random
    只淘汰设置了过期时间的key,采用随机删除

  • volatile-ttl
    删除过期字段设置为true和剩余最短生存时间(TTL)值的密钥。

缓存和数据库一致性

不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写数据库,都有可能出现数据不一致的情况。举一个例子:如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。

更新缓存有四种设计模式: Cache aside, Read through, Write through, Write behind caching

缓存更新模式

Cache Aside(旁路缓存)
  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:更新数据库, 更新/删除缓存

那么到底是更新缓存还是删除缓存呢?

如果读的频率高, 选择更新缓存, 这样可以提高缓存命中率

如果写的频率高, 选择删除缓存, 等到要读的时候再写入缓存, 可以减少无效的写操作和减少缓存数据量

Read Through(读穿)

Read Through 套路就是在查询操作中更新缓存,也就是说,当缓存失效的时候,Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。

Write Through(写穿)

Write Through 套路和Read Through相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

Write Behind Caching(写回)

在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比(因为直接操作内存嘛 ),因为异步,还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。但是,其带来的问题是,数据不是强一致性的,而且可能会丢失

一般而言(即不是特殊的业务场景), 我们选用Cache Aside(旁路缓存)模式, 下面我们的讨论也基于此模式.

操作缓存 & 操作数据库,哪个先?

针对删除缓存的情况:

如果采用先删除缓存,在更新数据库,会有什么后果?

  1. 线程A先把缓存的A数据删除,发生上下文切换
  2. 线程B想要读取A数据,发现缓存失效,从数据库读取A数据,并且更新缓存,
  3. 切换回线程A,线程A更新数据库数据。

至此,数据不一致的情况已经出现,数据库的写入较慢,容易出现此现象

先更新数据库,再删除缓存 怎么样?

  1. 线程A读取缓存A数据,发现缓存失效,所以去数据库读取,此时还未写入缓存,发生上下文切换
  2. 线程B更新数据库,并且删除对应的缓存,
  3. 切换回线程A,线程A将读取到的旧值,写入缓存

又出现了数据不一致的情况,但是相比先删除缓存,发生概率小很多,必须同时满足:

  • 读请求的时候,缓存刚好失效
  • 读请求 与 写请求 并发

所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存,请求 A 才更新完缓存的情况。所以推荐先更新数据库,再删除缓存

还是存在不一致的可能,怎么实现最终的一致性呢?

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

不采用先更新数据库,在删除缓存的方案, 而是用先删除缓存,再更新数据库,里面的不一致问题,用延迟双删解决
1、先删除缓存 2、再写数据库 3、休眠500ms(统计线程读取数据和写缓存的时间) (休眠的作用是当前线程等其他线程读完了数据后写入缓存后,删除缓存) 4、再删除缓存

  1. 设置较短的缓存过期时间
    沿用先更新数据库,再删除缓存,就给缓存设置一个较短的过期时间(只要它够短,就不容易读到错误数据)
针对更新缓存的情况:(面试说上面那种即可)

如果采用先更新缓存,再更新数据库

  1. 线程A更新了缓存,此时发生上下文切换
  2. 线程B更新缓存,更新数据库,此时切换回线程A
  3. 线程A更新数据库

此时就出现了数据不一致的现象,数据库的写入较慢,容易出现此现象。

反过来,先更新数据库,再更新缓存呢?

  1. 线程A更新了数据库,还未写入缓存,发生上下文切换
  2. 线程B更新了数据库,并且写入缓存,此时切换到线程A
  3. 线程A把缓存写入,覆盖了之前线程B的缓存

至此,数据不一致的情况,又出现了。缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现请求 B 已经更新了数据库并且更新了缓存,请求 A 才更新完缓存的情况。所以推荐先更新数据库,再更新缓存

保证操作原子性

操作数据库和操作缓存其实是两个操作,前面的所有分析都是建立在这两个操作都能同时执行成功,只要出现一个执行成功,一个执行失败的情况,就会造成数据库和缓存数据不一致的问题,会对敏感业务造成影响。下面就是两种解决方案.

队列+重试机制

请添加图片描述

  • 更新数据库数据;
  • 缓存因为种种问题删除失败
  • 将需要删除的key发送至消息队列
  • 自己消费消息,获得需要删除的key
  • 继续重试删除操作,直到成功

该方案有一个缺点,会对业务线代码造成大量的侵入。

基于订阅binlog的同步机制

请添加图片描述

本方案启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。关键是使用canal框架订阅binlog

  • 要开启mysql的binlog,需要设置binlog_format为ROW模式,并且设置server_id,保证唯一性。修改my.cnf配置文件,重启mysql服务。
[mysqld]
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
  • 查看是否修改 Binlog 成功。
# 查看 binlog 日志是否开启
show variables like 'log_%';
  • MySQL 执行 SQL 语句创建 canal 单独使用的账号,用来进行 Binlog 的同步和监听
CREATE USER canal IDENTIFIED BY 'canal';  
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
Reference:
  1. 缓存更新的套路
  2. Java全栈知识体系

欢迎访问我的个人博客www.levitategu.cn

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

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

相关文章

多路递归的一些算法题

前言 首先我想讲一下&#xff0c;我对多路递归的理解吧&#xff0c;我认为多路递归就是循环中套回调&#xff0c;对于循环有几次就是几叉树&#xff0c;就好比我们常用的二叉树的dfs(node.left) 和 dfs(node.right)等前中后序遍历&#xff0c;也就是for (int i 0; i < 2; …

AIGC - SD(中英文本生成图片) + PaddleHub/HuggingFace + stable-diffusion-webui

功能 stable-diffusion(文本生成图片)webui-win搭建&#xff08;开启api界面汉化&#xff09;PaddleHubHuggingFace: SD2&#xff0c;中文-alibaba/EasyNLP stable-diffusion-webui 下载与安装 环境相关下载 python&#xff08;文档推荐&#xff1a;Install Python 3.10.6 …

区块链技术与应用学习笔记(1-4节)——北大肖臻课程

目录 1. 区块链初识(课程简介&#xff09; 被过度炒作&#xff0c;落地应用有限&#xff1f; 下一代的价值互联网&#xff1f;世界上最慢的数据库&#xff1f; 2. BTC-密码学原理&#xff08;比特币&#xff09; 1)哈希 哈希函数特点 个人学习所得 2)签名 个人对于…

U-boot 21.10 启动流程梳理

目录 背景平台启动入口确认启动源码DuoS_SG2000_RISCVLubancat2_RK3568_ARM 初始化流程board_init_fboard_init_r 参考 背景 设备&#xff1a;MilkV Duo S 版本&#xff1a;U-boot 2021.10 编译命令 # Milkv-DuoS SD卡版本&#xff0c;对应[board]与[config]分别为&#xff1…

Leetcode_相交链表

✨✨所属专栏&#xff1a;LeetCode刷题专栏✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 题目&#xff1a; 题解&#xff1a; 看到这个题目首先我们要排除链表逆置的想法&#xff0c;如图、因为c1节点只有一个next指针&#xff0c;逆置后不可能同时指向a2和b3节点。 其次有的的同学…

24深圳杯AC题完整思路+可执行代码+参考论文!!!!

比赛题目的完整版思路可执行代码数据参考论文都会在第一时间更新上传的&#xff0c;大家可以参考我往期的资料&#xff0c;所有的资料数据以及到最后更新的参考论文都是一次付费后续免费的。注意&#xff1a;&#xff08;建议先下单占坑&#xff0c;因为随着后续我们更新资料数…

【AIGC调研系列】大型语言模型如何减少幻觉生成

在解读大型语言模型&#xff08;LLMs&#xff09;中的长格式事实性问题时&#xff0c;我们首先需要认识到这些模型在生成内容时可能会产生与既定事实不一致的情况&#xff0c;这种情况通常被称为“幻觉”[2][3]。这种现象不仅可能导致信息的误传&#xff0c;还可能对社会造成误…

新时代凌迟:考研

我不喜欢上班&#xff0c;但我很欣赏老板的品味&#xff0c;因为咱们公司竟然还在订阅报纸&#xff0c;而且只有一份&#xff0c;《中国青年报》。 这份报纸我最喜欢看的是“冰点周刊”专栏&#xff0c;因为这个栏目能让读者相信&#xff1a;报纸远远可以超越一天的生命。 昨天…

跨境代买淘宝系统,跨境代采系统,淘宝代购系统,淘宝代购集运系统,1688代采系统

淘宝代购系统是一种集成的电商平台服务&#xff0c;主要针对海外用户提供购买中国大陆商品的便利通道。以下是其核心功能与特点&#xff1a; 多语言支持&#xff1a;为了满足全球用户的需求&#xff0c;代购系统提供多语言界面&#xff0c;让不同国家和地区的用户都能方便地浏…

c++初阶——类和对象(下)

大家好&#xff0c;我是小锋&#xff0c;今天我们来学习我们类和对象的最后一个章节&#xff0c;我们本期的内容主要是类和对象的一些细节进行讲解 再谈构造函数 我们在初始化时有两种方式一种是函数体内初始化&#xff0c;一种是初始化列表 我们先来看看日期类的初始化 构造…

Web3技术解析:区块链在去中心化应用中的角色

引言 在过去几年中&#xff0c;Web3技术已经成为了互联网领域的一个热门话题。作为区块链技术的延伸&#xff0c;Web3不仅仅是数字货币的代名词&#xff0c;更是一个能够为各种应用提供去中心化解决方案的强大工具。本文将深入探讨区块链在Web3去中心化应用中的关键角色&#…

【考研数学】武忠祥考研课优缺点大盘点

虽然每年大家推荐的最多的是张宇和汤家凤&#xff0c;但是我强烈推荐武忠祥老师&#xff01; 武忠祥老师真宝藏老师&#xff0c;他讲课不像张宇老师那样段子频出&#xff0c;也不想汤家凤老师那样&#xff0c;武忠祥老师有自己的方法论&#xff0c;真的有点东西&#xff0c;武…

一周学会Django5 Python Web开发-Django5 ORM执行SQL语句

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计49条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

AI工具集:解锁智能新境界,一站式解决你的所有需求!

在这个信息爆炸的时代&#xff0c;我们每天都在与大量的数据和信息打交道。如何高效地处理这些信息&#xff0c;提高工作效率和生活品质&#xff0c;成为了我们亟待解决的问题。而AI工具集(AI-321.com)的出现&#xff0c;无疑为我们提供了一把解锁智能新境界的钥匙。 AI-321 | …

基于streamlit快速部署机器学习项目(Public URL)

基于streamlit的AIGC项目前端展示 1.Streamlit 简介与入门1.1 安装 Streamlit1.2 开发Streamlit应用程序1.3 启动并运行1.3.1 本地运行1.3.2 部署 现在LLM技术发展迅速&#xff0c;很多人在学习的时候&#xff0c;都想展示效果&#xff0c;并且想部署在服务器上&#xff0c;但是…

Zephyr sensor子系统学习

一、背景 2023年7月份nRF Connect SDK 2.4.0最新版本&#xff0c;使用的Zephyr V3.3版本。从Zephyr 3.5版本在子系统中加入了sensing子系统。 现在最新的nRF Connect SDK 2.6.0 release支持v3.5.99-ncs1&#xff0c;已经支持sensing子系统 nRF52840现在官方支持两个传感器de…

Rust中的函数指针

什么是函数指针 通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 fn &#xff08;使用小写的 ”f” &#xff09;以免与 Fn 闭包 trait 相混淆。fn 被称为 函数指针&#xff08;function pointer&#xff09;。指定参数为函数指针的语法类似于闭包。 函数指…

阿里前端常考vue面试题汇总_阿里高级vue面试题

改变 ![](https://img-blog.csdnimg.cn/img_convert/b736620bcd29f08f3685022ab5583d8b.webp?x-oss-processimage/format,png)你会发现&#xff0c; **只有改变的栏目才闪烁&#xff0c;也就是进行重绘** &#xff0c;数据没有改变的栏目还是保持原样&#xff0c;这样就大大节…

(3)C程序可执行文件的生成过程

原文链接&#xff1a;https://www.jianshu.com/p/b7e44f749211 一、可执行文件的生成 我们先通过一个简单C程序&#xff0c;回顾一下可执行文件的生成过程。 ​​​​​​​ ​​​​​​​ 可执行文件的生成过程如下图&#xff1a; 如图&#xff0c;可执行文…

Java学习路线及自我规划

荒废了一段时间&#xff0c;这段时间的总结开始了JavaWeb的学习但是困难重重&#xff0c;例如Maven&#xff0c;Vue的路由等&#xff0c;所以我反省了一段时间&#xff0c;因为基础薄弱&#xff0c;加之学习的资源是速成视频&#xff0c;导致大厦将倾的局面&#xff08;也算不上…