引言

在现代互联网架构中,缓存是提升系统性能、降低数据库压力的核心手段之一。而 Redis 作为高性能的内存数据库,凭借其丰富的数据结构、灵活的配置选项以及高效的网络模型,已经成为缓存领域的首选工具。本文将从 Redis 的基本原理出发,深入探讨其在缓存场景中的核心概念、设计模式、常见问题及解决方案,并结合高可用架构的设计思路,帮助读者全面掌握 Redis 缓存的技术要点。

一、Redis 与缓存的基本原理

1.1 什么是 Redis?

Redis(Remote Dictionary Server)是一个开源的内存键值存储系统,支持多种数据结构(如 String、Hash、List、Set、Sorted Set 等)。它通过内存操作实现极低的延迟(通常为微秒级),同时提供持久化机制和主从复制功能,确保数据的可靠性和高可用性。

1.2 缓存的核心作用

缓存的本质是通过空间换时间,将高频访问的数据存储在内存中,减少对底层数据库的直接请求。其核心优势包括:

  • 降低数据库负载:通过缓存热点数据,减少数据库的查询压力。
  • 提高响应速度:内存读取速度远高于磁盘,显著缩短请求响应时间。
  • 应对突发流量:在秒杀、促销等场景中,缓存可以有效吸收瞬时高并发请求。

1.3 缓存的缺点

但是缓存也会增加代码复杂度和运营的成本:

二、如何使用缓存

实际开发中,会构筑多级缓存来使系统运行速度进一步提升,例如:本地缓存与redis中的缓存并发使用

浏览器缓存:主要是存在于浏览器端的缓存

应用层缓存:可以分为tomcat本地缓存,比如之前提到的map,或者是使用redis作为缓存

数据库缓存:在数据库中有一片空间是 buffer pool,增改查数据都会先加载到mysql的缓存中

CPU缓存:当代计算机最大的问题是 cpu性能提升了,但内存读写速度没有跟上,所以为了适应当下的情况,增加了cpu的L1,L2,L3级的缓存

三、缓存更新策略

3.1 介绍

缓存更新是redis为了节约内存而设计出来的一个东西,主要是因为内存数据宝贵,当我们向redis插入太多数据,此时就可能会导致缓存中的数据过多,所以redis会对部分数据进行更新,或者把他叫为淘汰更合适。

内存淘汰:redis自动进行,当redis内存达到咱们设定的max-memery的时候,会自动触发淘汰机制,淘汰掉一些不重要的数据(可以自己设置策略方式)

超时剔除:当我们给redis设置了过期时间ttl之后,redis会将超时的数据进行删除,方便咱们继续使用缓存

主动更新:我们可以手动调用方法把缓存删掉,通常用于解决缓存和数据库不一致问题

3.2 数据库缓存不一致解决方案:

由于我们的缓存的数据源来自于数据库,而数据库的数据是会发生变化的,因此,如果当数据库中数据发生变化,而缓存却没有同步,此时就会有一致性问题存在,其后果是:

用户使用缓存中的过时数据,就会产生类似多线程数据安全问题,从而影响业务,产品口碑等

综合考虑我们采用人工编码方式来解决数据库和缓存不一致的问题。

        人工编码方式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案。

操作缓存和数据库时有三个问题需要考虑:

删除缓存还是更新缓存?

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

  • 删除缓存:更新数据库时让缓存失效,查询时再更新缓存

如果采用第一个方案,那么假设我们每次操作数据库后,都操作缓存,但是中间如果没有人查询,那么这个更新动作实际上只有最后一次生效,中间的更新动作意义并不大,我们可以把缓存删除,等待再次查询时,将缓存中的数据加载出来。

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

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

  • 分布式系统,利用TCC等分布式事务方案

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

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

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

        应该具体操作缓存还是操作数据库,我们应当是先操作数据库,再删除缓存,原因在于,如果你选择第一种方案,在两个线程并发来访问时,假设线程1先来,他先把缓存删了,此时线程2过来,他查询缓存数据并不存在,此时他写入缓存,当他写入缓存后,线程1再执行更新动作时,实际上写入的就是旧的数据,新的数据被旧数据覆盖了。

四、缓存穿透:无效请求的“黑洞”

4.1 问题定义

缓存穿透是指请求的数据既不存在于缓存中,也不存在于数据库中,导致请求直接穿透到数据库。这种现象会显著增加数据库的负载,甚至引发服务不可用。

4.2 常见场景

  • 恶意攻击:黑客通过伪造非法 ID(如随机字符串)持续请求,消耗数据库资源。
  • 业务逻辑漏洞:未校验用户输入的合法性,导致非法请求直接访问数据库。
  • 冷启动数据:新业务上线初期,缓存尚未填充,所有请求均需查询数据库。

4.3 解决方案

(1)布隆过滤器(Bloom Filter)
  • 原理:使用位数组和哈希函数快速判断数据是否存在。
  • 优势
    • 内存占用极小(如百万级数据仅需几 MB)。
    • 拦截效率高(哈希计算时间复杂度为 O(1))。
  • 局限性
    • 存在误判率(需合理设置哈希函数数量和位数组大小)。
    • 不支持删除操作(需使用 Counting Bloom Filter)。
(2)缓存空值(Null Caching)
  • 原理:对数据库中不存在的数据返回空值,并设置短 TTL(如 5 分钟)。
  • 优势
    • 实现简单,无需额外数据结构。
    • 有效拦截恶意请求。
  • 缺点
    • 占用缓存空间(需评估空值占比)。
    • 短时间内可能被高频请求反复触发。

缓存空对象思路分析:当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库,简单的解决方案就是哪怕这个数据在数据库中也不存在,我们也把这个数据存入到redis中去,这样,下次用户过来访问这个不存在的数据,那么在redis中也能找到这个数据就不会进入到缓存了。

布隆过滤:布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数据后,再将其放入到redis中,

假设布隆过滤器判断这个数据不存在,则直接返回

这种方式优点在于节约内存空间,存在误判,误判原因在于:布隆过滤器走的是哈希思想,只要哈希思想,就可能存在哈希冲突

五、缓存雪崩

        缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

  • 给不同的Key的TTL添加随机值

  • 利用Redis集群提高服务的可用性

  • 给缓存业务添加降级限流策略

  • 给业务添加多级缓存

        

六、缓存击穿

6.1解决方案

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

常见的解决方案有两种:

  • 互斥锁

  • 逻辑过期

逻辑分析:假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的,此时只要线程1走完这个逻辑,其他线程就都能从缓存中加载这些数据了,但是假设在线程1没有走完的时候,后续的线程2,线程3,线程4同时过来访问当前这个方法, 那么这些线程都不能从缓存中查询到数据,那么他们就会同一时刻来访问查询缓存,都没查到,接着同一时间去访问数据库,同时的去执行数据库代码,对数据库访问压力过大

解决方案一、使用锁来解决:

因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用tryLock方法 + double check来解决这样的问题。

假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。

解决方案二、逻辑过期方案

方案分析:我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我们不设置过期时间,其实就不会有缓存击穿的问题,但是不设置过期时间,这样数据不就一直占用我们内存了吗,我们可以采用逻辑过期方案。

我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个 线程去进行 以前的重构数据的逻辑,直到新开的线程完成这个逻辑后,才释放锁, 而线程1直接进行返回,假设现在线程3过来访问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把重建数据构建完后,其他线程才能走返回正确的数据。

这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。

6.2 进行对比

互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性能肯定受到影响

逻辑过期方案: 线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是在重构数据完成前,其他的线程只能返回之前的数据,且实现起来麻烦

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

耘瞳科技国产化点云处理软件,开启智能化三维测量新时代

在现代工业制造领域,三维点云数据已成为推动生产效率提升、质量控制优化以及智能制造转型的关键技术之一。三维点云数据能够提供高精度的物体表面信息,广泛应用于制造零件的质量检测;通过点云数据与CAD模型的对比分析,可以快速检测…

RabbitMQ面试精讲 Day 8:死信队列与延迟队列实现

【RabbitMQ面试精讲 Day 8】死信队列与延迟队列实现 文章标签 RabbitMQ,消息队列,死信队列,延迟队列,面试技巧,分布式系统 文章简述 本文是"RabbitMQ面试精讲"系列第8天,深入讲解死信队列与延迟队列的实现原理与实战应用。文章详细解析死信队列的触发…

团结引擎 1.5.0 版本发布:Android App View 功能详解

核心亮点 原生安卓应用支持 2D & 3D 双形态呈现 编辑器全流程集成 灵活调控功能 多应用并行展示 智能座舱应用示例 快速入门指南 开发说明 功能支持 实验性功能 资源链接 团结引擎 1.5.0 版本已于 4 月 14 日正式上线。本次更新中,车机版引入了一项突…

基于SpringBoot的OA办公系统的设计与实现

文章目录前言详细视频演示具体实现截图后端框架SpringBoot持久层框架MyBaits成功系统案例:代码参考数据库源码获取前言 博主介绍:CSDN特邀作者、985高校计算机专业毕业、现任某互联网大厂高级全栈开发工程师、Gitee/掘金/华为云/阿里云/GitHub等平台持续输出高质量…

知识随记-----用 Qt 打造优雅的密码输入框:添加右侧眼睛图标切换显示

Qt 技巧:通过 QLineEdit 右侧眼睛图标实现密码可见性切换 文章目录Qt 技巧:通过 QLineEdit 右侧眼睛图标实现密码可见性切换概要整体架构流程技术名词解释技术细节实现效果展示概要 本文介绍如何使用 Qt 框架为 QLineEdit 控件添加一个右侧的眼睛图标&a…

Unity里的对象旋转数值跳转问题的原理与解决方案

文章目录1. 问题描述2. 问题原因3. 解决方案3.1通过多个父子关系从而控制旋转(推荐)3.2 使用四元数进行旋转1. 问题描述 我们现在写一个3D的Unity程序,我们现在设置了一个物体后,我们想旋转使其改为我们想要的情况。但是我们如果…

为什么现代 C++ (C++11 及以后) 推荐使用 constexpr和模板 (Templates) 作为宏 (#define) 的替代品?​

我们用现实世界的比喻来深入理解​​为什么 C 中的宏 (#define) 要谨慎使用,以及为什么现代 C (C11 及以后) 推荐使用 constexpr 和模板 (Templates) 作为替代品。​​🧩 ​​核心问题:宏 (#define) 是文本替换​​想象宏是一个 ​​“无脑的…

PyCharm vs. VSCode 到底哪个更好用

在 Python 开发者中,关于 PyCharm 和 VSCode 的讨论从未停止。一个是功能齐备的集成开发环境(IDE),另一个是轻快灵活的代码编辑器。它们代表了两种不同的开发哲学,选择哪个,往往取决于你的项目需求、个人习…

FPGA学习笔记——VGA彩条显示

目录 一、任务 二、分析 三、代码 四、实验现象 五、更新 一、任务 使用VGA实现彩条显示,模式是640x48060。 二、分析 首先,模式是640x48060,那么对照以下图标,知道其它信息,不清楚时序和VGA扫描方式的可以看看这…

ES-301A :让 Modbus 设备无缝接入工业以太网的高效桥梁

在工业自动化领域,串口设备与以太网的互联互通是提升系统效率的关键。ES-301A 工业以太网串口网关作为上海泗博自动化精心打造的专业解决方案,以强大的协议转换能力、工业级可靠性和灵活配置特性,成为连接 Modbus RTU/ASCII 设备与 Modbus TC…

【学习笔记】FTP库函数学习

【学习笔记】FTP库函数学习 FTP基本指令步骤 1、初始化会话句柄:CURL *curl curl_easy_init(); 2、设置会话选项: 设置服务器地址,设置登录用户和密码 curl_easy_setopt(curl, CURLOPT_URL, ftp_server); curl_easy_setopt(curl, CURLOPT_US…

ARM Cortex-M异常处理高级特性详解

1. 异常处理概述 ARM Cortex-M处理器提供了高效的异常处理机制,包含多种硬件优化特性,显著提升了中断响应性能和系统效率。这些特性对于实时嵌入式系统和网络协议栈(如LwIP)的性能至关重要。 1.1 Cortex-M异常处理架构 Cortex-M异…

【图像算法 - 08】基于 YOLO11 的抽烟检测系统(包含环境搭建 + 数据集处理 + 模型训练 + 效果对比 + 调参技巧)

一、项目背景与需求 【打怪升级 - 08】基于 YOLO11 的抽烟检测系统(包含环境搭建 数据集处理 模型训练 效果对比 调参技巧)今天我们使用YOLO11来训练一个抽烟检测系统,基于YOLO11的抽烟检测系统。我们使用了大概两万张图片的数据集训练了…

vue2升级vue3中v-model的写法改造

vue2选项式 <template><div><el-rowclass"group-title":title"$t(restore_default_parameters)">{{ $t(restore_default_parameters) }}</el-row><el-form-item :label"$t(restore_default_parameters)" class"…

5G-LEO 简介

1. 什么是 5G-LEO 5G-LEO 指的是将 5G 新空口&#xff08;5G NR&#xff09;服务扩展到低轨卫星&#xff08;LEO&#xff09;上的非地面网络&#xff08;NTN, Non-Terrestrial Network&#xff09;方案。通过在距地面约500–2 000 km 的低轨道卫星上部署通信载荷&#xff0c;5G…

【MCAL】AUTOSAR架构下SPI数据同步收发具体实现

目录 前言 正文 1.依赖的SPI硬件特性 1.1. SPI时隙参数配置 1.2. SPI数据发送和接收模式 2.MCAL中的SPI配置 3.软件的具体实现 3.1. Spi_SyncTransmit 3.2. Spi_lSyncTransmit 3.3. Spi_lSyncStartJob 3.4. Spi_lSyncTransmitData8Bit 3.5. Spi_lSynTransErrCheck …

SQL157 更新记录(一)

描述现有一张试卷信息表examination_info&#xff0c;表结构如下图所示&#xff1a;FiledTypeNullKeyExtraDefaultCommentidint(11)NOPRIauto_increment(NULL)自增IDexam_idint(11)NOUNI(NULL)试卷IDtagchar(32)YES(NULL)类别标签difficultychar(8)YES(NULL)难度durationint(11…

悬赏任务系统小程序/APP源码,推荐任务/发布任务/会员服务

1. 我们承诺及优势本店源码承诺&#xff1a;1&#xff09;. 店长亲测 - 100%完整可运行2&#xff09;. 含详细安装文档3&#xff09;. 支持二次开发定制4&#xff09;. 专业客服随时解答5&#xff09;. 技术团队保障质量2. 功能详细说明主要功能 模块 角色 解释说明 用户登录和…

Ubuntu20.04系统上使用YOLOv5训练自己的模型-1

在Ubuntu系统上使用YOLOv5训练自己的模型&#xff0c;你需要遵循以下步骤。这里我将详细说明如何从准备数据集到训练模型的整个过程。 步骤 1: 安装依赖项 首先&#xff0c;确保你的Ubuntu系统上安装了Python、PyTorch和必要的库。你可以使用以下命令安装这些依赖项&#xff1a…

解决微信小程序中camera组件被view事件穿透触发对焦以及camera的bindtap事件

view跟camera组件同级 不要用bind:tap和catch:tap 替换用catch:touchstart即可解决&#xff01; 如果你不放心&#xff0c;可以再加个透明蒙版&#xff0c;这样就不会触发了&#xff01;&#xff08;不加这个也行&#xff0c;但是必须要用catch:touchstart&#xff09;<!-- …