当前位置: 首页 > 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;研究非线性动态反演控制器可以是一个有趣的课题。动态反演控制器的目标…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章

用 Rust 重写 Linux 内核模块实战&#xff1a;迈向安全内核的新篇章 ​​摘要&#xff1a;​​ 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言&#xff0c;受限于 C 语言本身的内存安全和并发安全问题&#xff0c;开发复杂模块极易引入难以…...

boost::filesystem::path文件路径使用详解和示例

boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类&#xff0c;封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解&#xff0c;包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...