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

Redis学习路线(4)—— Redis实现项目缓存

一、什么是缓存

(一)概念:缓存就是数据交换的缓冲区(称为Cache),是存储数据的临时区域,一般读写性能较高。

(二)常见缓存: 浏览器缓存,服务器缓存,数据库缓存,CPU缓存,磁盘缓存。

(三)缓存的作用:

  • 降低后端负载
  • 提高读写效率,降低响应时间

(四)缓存的成本:

  • 数据一致性成本
  • 代码维护成本
  • 运维成本

二、缓存更新策略

(一)三种更新策略的对比

内存淘汰超时剔除主动更新
说明Redis提供的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存给缓存数据添加TTL,到期后自动删除缓存,下次查询时更新缓存编写业务逻辑,在修改数据库的同时更新缓存
一致性一般
维护成本

(二)业务场景

  • 低一致性需求: 使用内存淘汰机制,主要是针对几乎不改变的数据。
  • 高一致性需求: 主动更新,并以超时剔除作为协助方案,主要是针对频繁查询的缓存。

(三)主动更新策略

  • (1)Cache Aside Pattern: 由缓存调用者,同时更新数据库和缓存。(可控性更高)
    • 删除缓存还是更新缓存?
      • 更新缓存: 每次更新数据库都更新缓存,无效写操作较多。
      • 删除缓存: 更新数据库时让缓存失效,查询时再更新缓存。
    • (2)如何保证缓存与数据库的操作同时成功或失败?
      • 单体系统: 将缓存与数据库操作放在一个事务。
      • 分布式系统: 利用TCC等分布式事务方案。
    • (3)先操作缓存还是先操作数据库?
      • 先删除缓存,在操作数据库。
      • 先操作数据库,在删除缓存。(保证一致性的概率更高)
  • Read/Write Through Pattern: 缓存与数据库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性问题。
  • Write Behind Caching Pattern: 调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致。

三、缓存穿透、缓存雪崩、缓存击穿

(一)缓存穿透

1、缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。

2、缓存穿透过程: 客户端发送不存在的id 》 Redis按照 id 查询(未查询到) 》 数据库根据 id 查询(未查询到) 》 返回客户端 》 客户端发送不存在的id。(这个过程若采用多线程循环攻击数据库,那么就会造成数据库带宽爆满,请求阻塞,系统资源耗尽等等问题)

3、缓存穿透常用解决方案:

  • (1)缓存空对象: 在用户第一次查询缓存和数据库时,都没有对应的值,则在缓存中存储一个对应的空值,并设置一个短期的TTL,当恶意用户短期内发起请求时,都会在缓存中查找到对应的值,避免了缓存穿透。
    • 优点: 实现简单,维护方便
    • 缺点: 额外的内存消耗、可能造成短期的不一致
  • (2)布隆过滤: 通过布隆过滤器,在客户端和Redis之间做一道防火墙,当用户访问到布隆过滤器时,布隆过滤器会查询自身是否存在请求值,若不存在则拒绝,若存在则放行查询。
    • 优点: 内存占用较少,没有多余key
    • 缺点: 实现复杂,存在误判可能
  • (3)增强id复杂度,避免被猜测id规律
  • (4)做好数据的基础格式校验
  • (5)加强用户权限校验
  • (6)做好热点参数的限流

4、布隆过滤器: 一种算法,通过将数据库的数据进行某一种hash值计算,并将这些hash值转化为二进制位存储到布隆过滤器中,客户端发送的请求中的数据,通过hash计算转化为二进制位在布隆过滤器中查找,若能找到则说明存在该数据。

(二)缓存雪崩

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

2、缓存雪崩的过程: 客户端请求数据 》 Redis查询(Redis部分缓存缺失或Redis宕机) 》 查询数据库(大量请求到达,把数据库也给宕机了)

3、解决方案:

  • (1)给不同的key的TTL添加随机值(避免同时失效)
  • (2)利用Redis集群提高服务的可用性(Redis哨兵机制监听选举主Redis,主从Redis保证数据安全性)
  • (3)给缓存业务添加降级限流策略
  • (4)给业务添加多级缓存

(三)缓存击穿

1、缓存击穿是指高并发缓存重建业务复杂的key突然失效,无数的请求访问在瞬间给数据库带来巨大的冲击。

2、常见的解决方案:

  • (1)互斥锁: 在一个线程进入缓存重建阶段时,获取互斥锁,其它线程无法进行获取互斥锁,并自旋尝试获取锁,直到互斥锁释放。
    • 缺点: 线程锁住了缓存,时间过长会造成业务阻塞。
  • (2)逻辑过期: 在存储一个数据时,添加一个逻辑上的过期时间,到期后直接移除即可。

3、两种解决方案的区别

解决方案优点缺点
互斥锁
  • 没有额外的内存消耗
  • 保证一致性
  • 实现简单
  • 线程需要等待,性能受影响
  • 可能发生死锁
逻辑过期
  • 现成无需等待,性能较好
  • 不保证一致性
  • 有额外内存消耗
  • 实现复杂

四、缓存工具封装

基于StringRedisTemplate封装一个缓存工具类。

  • 方法1: 将任意Java对象序列化为json并存储在string类型的key中,并设置TTL过期时间。
  • 方法2: 将任意Java对象序列化为json并存储在string类型的key中,并可以设置逻辑过期时间,用于处理缓存击穿问题。
  • 方法3: 根据指定key查询缓存,并反序列化为指定类型,利用缓存空值的方式解决缓存穿透问题。
  • 方法4: 根据指定key查询缓存,并反序列化为指定类型,需要利用逻辑过期解决缓存击穿问题。

(一)工具类

@Slf4j
@Component
public class CacheClient {private final StringRedisTemplate redisTemplate;public CacheClient(StringRedisTemplate redisTempalte) {this.redisTemplate = redisTempalte;}public String toJsonStr(Object value) {return JSONObject.toJSONString(value)}public <T> String toJsonObj(String json, Class<T> type) {return JSONObject.parseObject(json, type)}}

(二)实现缓存工具方法

方法1:序列化并设置TTL

	public void set(String key, Object data, Long time, TimeUnit unit) {redisTemplate.opsForValue().set(key, this.toJsonStr(data), time, unit);}

方法2:序列化并设置逻辑过期

	public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {//1、设置逻辑过期,RedisData -> (R Data, LocalDateTime dateTime)RedisData redisData = new RedisData(value, LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//2、RedisData 写入RedisredisTemplate.opsForValue().set(key, this.toJsonStr(redisData));}

方法3:缓存空值解决缓存穿透

	public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {//1、查看Cache是否命中String key = keyPrefix + id;String json = stringRedisTemplate.opsForValue().get(key);if(!json.isEmpty()){//命中,判断为有值,直接返回return this.toJsonObj(json, type);}if(Objects.isNull(json)){//命中,判断为空白,返回错误信息return null;}//未命中,查看数据库R r = dbFallback.apply(id);//若不存在对应数据,则打入Redis一个空值,并返回错误信息if (r == null) {redisTemplate.opsForValue().set(key, "", 2, TimeUnit.MINUTES);return null;}//若存在,则返回并设置TTLthis.set(key, r, time, unit);//返回该值return r;}

方法4: 逻辑过期解决缓存击穿

public <R, ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){//1、查看Cache是否命中String key = keyPrefix + id;String json = stringRedisTemplate.opsForValue().get(key);if(Objects.isEmpty(json)){//命中,判断为空白,返回错误信息return null;}RedisData redisData = this.toJsonObject(json, Redis.Class);R r = type.cast(redisData.getData());LocalDateTime time = redisData.getDateTime();//若逻辑时间小于现实时间则返回当前对象if(time.isBefore(LocalDateTime.now())){redisData = JSON.parseObject(json, RedisData.class);if(redisData.getDateTime < LocalDateTime.now()){return type.cast(redisData.getData());}}//过期了,获取互斥锁String lockKey = "lock:shop:"+id;boolean isLock = tryLock(lockKey);//尝试获取互斥锁,如果事变,则睡眠重试if(!isLock){//失败则休眠后重试Thread.sleep(50);return queryWithLogicalExpire(keyPrefix, id, type, dbFallback, time, unit);}// 重建数据Executors.newFixedThreadPool(10).submit(() -> {try{//	查询数据库R r1 = dbFallback.apply(id);this.setWithLogicalExpire(id, r1, time, unit);}catch(Exception e){throw new RuntimeException(e);}finally{unlock(lockKey);}});return r;
}private boolean tryLock(String key) {Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "", 10, TimeUnit.SECONDS);return Boolean.getBoolean(flag);
}private void unlock(String key) {redisTemplate.delete(key);
}

相关文章:

Redis学习路线(4)—— Redis实现项目缓存

一、什么是缓存 &#xff08;一&#xff09;概念&#xff1a;缓存就是数据交换的缓冲区&#xff08;称为Cache&#xff09;&#xff0c;是存储数据的临时区域&#xff0c;一般读写性能较高。 &#xff08;二&#xff09;常见缓存&#xff1a; 浏览器缓存&#xff0c;服务器缓…...

【Unity造轮子】实现一个类csgo的武器轮盘功能

文章目录 前言素材导入开始1.放背景和中间的圆圈&#xff0c;调整合适的宽高和位置2.添加选择图像框3.添加一些武器道具选择4.书写脚本RadialMenuManager5.绑定脚本和对象6.运行效果&#xff0c;按tab键开启关闭轮盘7.优化添加显示选中的武器文本8.添加鼠标选中放大的效果9.添加…...

代码随想录算法训练营第三十天 | 单调栈系列复习

单调栈系列复习 每日温度未看解答自己编写的青春版重点题解的代码日后再次复习重新写 下一个更大元素 I未看解答自己编写的青春版重点题解的代码日后再次复习重新写 下一个更大元素II未看解答自己编写的青春版重点题解的代码日后再次复习重新写 接雨水未看解答自己编写的青春版…...

redis数据未到过期时间被删除

1. 问题描述 使用了jeecgboot开发后端代码&#xff0c;代码设置的redis过期时间为24小时&#xff0c;部署使用的宝塔面板&#xff0c;在redis中看到的过期时间也是为24小时&#xff0c;但是并未到过期时间&#xff0c;数据就被删除。 2. 解决办法 观察了一下redis中的数据&a…...

32.选择器

选择器 html部分 <div class"toggle-container"><input type"checkbox" id"good" class"toggle"><label for"good" class"label"><div class"ball"></div></label&…...

Linux--验证命令行上运行的程序的父进程是bash

1.输入以下代码&#xff1a; #include <stdio.h> #include <unistd.h> int main() {printf("hello world: pid: %d, ppid: %d\n",getpid(),getppid());return 0; }2.编译得到可执行程序​​​ 3.运行得到ppid 4.输入指令 ps axj | head -1 &&am…...

MySQL数据库关于表的一系列操作

MySQL中的数据类型 varchar 动态字符串类型&#xff08;最长255位&#xff09;&#xff0c;可以根据实际长度来动态分配空间&#xff0c;例如&#xff1a;varchar(100) char 定长字符串&#xff08;最长255位&#xff09;&#xff0c;存储空间是固定的&#xff0c;例如&#…...

Spring基于注解管理bean及全注解开发

文章目录 spring概述Spring定义Spring核心Spring Framework的特点 基于注解管理bean依赖开启组件扫描使用注解定义Bean案例:Autowired注入属性注入set注入形参上注入只有一个构造函数&#xff0c;无注解Autowire注解和Qualifier注解联合 Resource注入Spring全注解开发 spring概…...

QtC++ 技术分析3 - IOStream

目录 iostreamscanf/printfiostream 整体架构流相关类流缓冲区 模板特化后整体结构文件流文件流对象创建常见文件流操作输出格式设定文件流状态 字符串流字符串流内部缓冲区字符串流使用 流缓冲区用户自定义 IO iostream scanf/printf 几种常见的输入输出流函数 scanf 从键盘…...

2023年Q2京东环境电器市场数据分析(京东数据产品)

今年Q2&#xff0c;环境电器市场中不少类目表现亮眼&#xff0c;尤其是以净水器、空气净化器、除湿机等为代表的环境健康电器。此外&#xff0c;像冷风扇这类具有强季节性特征的电器也呈现出比较好的增长态势。 接下来&#xff0c;结合具体数据我们一起来分析Q2环境电器市场中…...

TCP/UDP的首部

TCP/UDP首部信息 TCP首部第一个4字节第二个4字节与第三个4字节第四个4字节第五个4字节选项最大报文段长度&#xff08;MSS&#xff09;选项窗口扩大选项时间戳选项 什么时候发送RST包UDP首部 TCP首部 TCP 首部长度为20字节&#xff0c;加上选项部分最大可达60字节。 第一个4…...

Kubernetes(K8s)从入门到精通系列之四:K8s的基本概念和术语之集群类

Kubernetes K8s从入门到精通系列之四:K8s的基本概念和术语之集群类 一、Master二、Node三、命名空间集群表示一个由Master和Node组成的K8s集群。 一、Master Master指的是集群的控制节点。在每个K8s集群都需要有一个或一组被称为Master的节点,来负责整个集群的管理和控制。M…...

黑马头条---day1

手机端查看 docker 容器&#xff0c;镜像操作命令 1、docker删除所有镜像命令 删除所有镜像的命令是Docker中一个非常常见的操作。下面是具体的实现步骤和命令示例&#xff1a; $ docker stop $(docker ps -aq) 停止所有正在运行的容器。 $ docker rm $(docker ps -aq) 删…...

【序列化工具JdkSerialize和Protostuff】

序列化工具对比 JdkSerialize&#xff1a;java内置的序列化能将实现了Serilazable接口的对象进行序列化和反序列化&#xff0c; ObjectOutputStream的writeObject()方法可序列化对象生成字节数组 Protostuff&#xff1a;google开源的protostuff采用更为紧凑的二进制数组&#…...

C++ 多线程编程导论(下)

文章目录 参考资料线程安全&#xff08;续&#xff09;门闩与屏障——latch 对象与 barrier 对象门闩&#xff08;latch&#xff09;屏障&#xff08;barrier&#xff09; 一次性调用——once_flag 对象与 call_once 函数 异步任务未来与承诺——future 对象与 promise 对象fut…...

Java并发系列之一:JVM线程模型

什么是线程模型&#xff1a; Java字节码运行在JVM中&#xff0c;JVM运行在各个操作系统上。所以当JVM想要进行线程创建回收这种操作时&#xff0c;势必需要调用操作系统的相关接口。也就是说&#xff0c;JVM线程与操作系统线程之间存在着某种映射关系&#xff0c;这两种不同维…...

容灾独家技术揭秘:HyperBDR无主机数据同步技术

01、一对一单机热备-传统灾备方式 单机热备是一种备份解决方案&#xff0c;它使用两台服务器来确保高可用性&#xff0c;是市场上最为常见的灾备模式。 在单机热备中&#xff0c;一台主服务器和一台备用服务器保持同步&#xff0c;以确保在主服务器出现故障或宕机时可以立即切换…...

FANUC机器人SRVO-050碰撞检测报警和SRVO-053干扰值过大故障报警总结

FANUC机器人SRVO-050碰撞检测报警和SRVO-053干扰值过大故障报警总结 前面和大家分享了关于SRVO-050碰撞检测报警和SRVO-053干扰值过大的原因分析以及处理方法,感兴趣的朋友可以参考以下链接中的内容: FANUC机器人SRVO-050碰撞检测报警原因分析及处理对策...

微信如何提高回复信息速度?

规范流程话术有什么用&#xff1f;为了提高回复客户的效率和质量&#xff0c;可以事先设计好的一套标准化的对话模板。它通常包括多个环节和问题&#xff0c;帮助客服人员或销售人员在与客户沟通时&#xff0c;按照标准化的流程进行&#xff0c;以提高工作效率和客户满意度。 如…...

模拟Stevens Lewis描述的小型飞机纵向动力学的非线性动态反演控制器研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码实现 &#x1f4a5;1 概述 针对Stevens和Lewis描述的小型飞机纵向动力学的非线性动态&#xff0c;研究非线性动态反演控制器可以是一个有趣的课题。动态反演控制器的目标…...

STM32矩阵按键详解——4×4行列扫描与非阻塞消抖(硬件总结六)

前言 独立按键虽然简单&#xff0c;但当产品需要十几个按键时&#xff0c;每个按键独占一个GPIO的接法就变得很不经济。矩阵按键通过“行列”的交叉结构&#xff0c;仅用NM个GPIO即可驱动NM个按键。以最常见的44矩阵为例&#xff0c;16个按键仅需8个GPIO&#xff0c;引脚利用率…...

离子阱量子变分算法原理与优化实践

1. 离子原生量子变分算法解析在量子计算领域&#xff0c;变分量子算法&#xff08;VQA&#xff09;已成为解决组合优化问题的主流方法。这类算法的核心在于设计高效的参数化量子线路&#xff08;ansatz&#xff09;&#xff0c;而传统方法通常依赖于大量纠缠门的组合。离子阱量…...

基于java的畅阅读系统小程序设计与实现(源码+数据库+文档)

畅阅读系统小程 目录 基于java的畅阅读系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&a…...

2026网盘横评:国民级云盘领衔,这几款备选也值得一看

前言作为长期接触AI资源、代码项目、大文件存储的从业者&#xff0c;日常高频使用各类网盘。很多朋友都会纠结主流网盘该如何选择&#xff0c;不同产品的存储能力、传输表现、功能适配差距明显。本文摒弃夸张测评&#xff0c;以客观分享的视角&#xff0c;从传输、存储、功能、…...

大模型实战:AgentScope ReActAgent 多智能体框架实战指南,小白程序员必备收藏!

本文介绍了如何利用 AgentScope 框架及其新版本 Spring AI Alibaba 来构建基于大模型的多智能体应用。文章首先强调了从单智能体优先原则出发&#xff0c;然后详细阐述了 AgentScope 支持的多智能体模式&#xff0c;包括 Pipeline、Routing、Skills、Subagents、Supervisor、Ha…...

CMake基础:常用内部变量和环境变量的引用

目录 1.常用 CMake 变量 1.1.编译与构建控制 1.2.路径与目录变量 1.3.项目信息变量 1.4.系统与平台变量 1.5.工具链与交叉编译 1.6.测试与安装变量 1.7.高级编译选项 2.常用环境变量 2.1.编译器与工具链 2.2.依赖库路径 2.3.CMake 专用环境变量 2.4.系统环境变量P…...

SleeperX:macOS系统级电源管理架构解析与深度集成方案

SleeperX&#xff1a;macOS系统级电源管理架构解析与深度集成方案 【免费下载链接】SleeperX MacBook prevent idle/lid sleep! Hackintosh sleep on low battery capacity. 项目地址: https://gitcode.com/gh_mirrors/sl/SleeperX 在macOS生态系统中&#xff0c;电源管…...

如何将企业微信 RPA 抽象为高可用的外部群自动化 API?

在做企业微信外部群&#xff08;如跨群互动、自动化精准群发、批量建群&#xff09;的自动化能力时&#xff0c;业界通常面临两种选型&#xff1a;一种是直接攻克底层协议&#xff0c;但面临极高的安全风控与多变协议的维护成本&#xff1b;另一种是基于 RPA&#xff08;机器人…...

如何快速掌握uesave:Unreal引擎存档编辑的完整指南

如何快速掌握uesave&#xff1a;Unreal引擎存档编辑的完整指南 【免费下载链接】uesave Rust library and CLI to read and write Unreal Engine save files 项目地址: https://gitcode.com/gh_mirrors/ue/uesave uesave是一款专门用于处理Unreal引擎游戏存档文件的开源…...

别再傻等!解决conda install nb_conda卡在solving environment的3个高效方法(附清华源配置)

彻底解决conda install卡在solving environment的终极指南 当你满怀期待地在终端输入conda install nb_conda准备为Jupyter Notebook添加环境管理功能时&#xff0c;却发现进度条永远卡在"solving environment"这一步&#xff0c;这种体验就像在高速公路上遇到无休止…...