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);
}

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

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

相关文章

SpringBootAdmin介绍

一、SpringBootAdmin 简介 1.1 概述 SpringBootAdmin 是一个非常好用的监控和管理的开源组件&#xff0c;该组件能够将 Actuator 中的信息进行界面化的展示&#xff0c;也可以监控所有 Spring Boot 应用的健康状况&#xff0c;提供实时警报功能。 1.2 功能特性 显示应用程序…

智能家居是否可与ChatGPT深度融合?

​ ChatGPT自2022年面世以来&#xff0c;已为亿万网民提供智能问答服务。然而我们是否曾想到&#xff0c;这一人工智能驱动的聊天机器人&#xff0c;是否可为智能家居赋能? 要实现ChatGPT与智能家居设备之间的无缝对话&#xff0c;单单依靠一台终端是远远不够的。ChatGPT必须…

Mybatis中where 1=1 浅析

在一些集成mybatis的工程中经常看到where11 的代码&#xff0c;也有同事问我&#xff0c;这样写有什么用&#xff0c;下面对其进行简单的分析记录一下。 1、场景 看下面这样一段xml中的代码 <select id"queryBook" parameterType"com.platform.entity.Book…

ST官方基于米尔STM32MP135开发板培训课程(一)

本文将以Myirtech的MYD-YF13X以及STM32MP135F-DK为例&#xff0c;讲解如何使用STM32CubeMX结合Developer package实现最小系统启动。 1.开发准备 1.1 Developer package准备 a.Developer package下载&#xff1a; ‍https://www.st.com/en/embedded-software/stm32mp1dev.ht…

使用powershell找回丢失的RDCManage密码

内网的一台服务器上的装机默认用户密码忘记了&#xff0c;但是好在别的电脑上使用RDCMan&#xff08;Remote Desktop Connection Manager&#xff09;连接过这台服务器&#xff0c;并且保存了密码。于是经过一番折腾&#xff0c;最后把密码找回来了&#xff1a; 最后成功的powe…

个人建议(2023-7-31)【建议】

前言 2023-7-31 21:54:54 我的个人建议又㕛叒叕来了 关键重点黑体标出 本文已在CSDN博客中发布文章 本文已在CSDN建议社区中发布帖子 最近比较忙只能简单写一写 收货地址&#xff0c;浏览之后再确认&#xff0c;老是怕提交错误 快递进度查询 草稿也能设置专栏 不用重新…

同步锁: synchronized

synchronized 1. synchronized的特性2. synchronized的使用3. synchronized的锁机制 1. synchronized的特性 原子性: 所谓原子性就是指一个操作或者多个操作&#xff0c;要么全部执行并且执行的过程不会被任何因素打断&#xff0c;要么就都不执行。可见性: 可见性是指多个线程…

IO流简述

IO流IO流使用场景 什么是IO流常用的IO流字节流字符流缓冲流 BIO、NIO、AIO的区别 IO流 IO流使用场景 如果操作的是纯文本文件&#xff0c;优先使用字符流如果操作的是图片、视频、音频等二进制文件。优先使用字节流如果不确定文件类型&#xff0c;优先使用字节流。字节流是万能…

如何使用Flask-SQLAlchemy来管理数据库连接和操作数据?

首先&#xff0c;我们需要安装Flask-SQLAlchemy。你可以使用pip来安装它&#xff0c;就像这样&#xff1a; pip install Flask-SQLAlchemy好了&#xff0c;现在我们已经有了一个可以操作数据库的工具&#xff0c;接下来让我们来看看如何使用它吧&#xff01; 首先&#xff0c…

前端接收后端返回换行符 /n 不生效

问题&#xff1a;前端接收后端返回换行符 /n 不换行、不生效 解决方案&#xff1a; 一、 设置css的white-space属性 <div class"text-container">{{ text }}</div>export default {data() {return {text: 这是第一行\n这是第二行,};}, };<style sco…

php-golang-rpc 简单的jsonrpc实践

golang代码&#xff1a; package main import ( "net" "net/rpc" "net/rpc/jsonrpc" ) type App struct{} type Res struct { Code int json:"code" Msg string json:"msg" Data any json:"data" } fun…

Qu‘est-ce Que C‘est? 2023牛客暑期多校训练营4-J

登录—专业IT笔试面试备考平台_牛客网 题目大意&#xff1a;求满足如下条件的长度为n的数组数量&#xff1a;所有数字的范围在-m~m之间&#xff0c;任意连续的元素数量>1的区间的的区间和>0 1<n,m<5000; 思路&#xff1a;要让所有区间元素和>0&#xff0c; 我…

上门居家养老小程序社区养老小程序开发方案详解

居家养老管理社区养老小程序有哪些功能呢&#xff1f; 1.选择养老服务类型 医疗护理&#xff0c;家政服务预约&#xff0c;上门助浴、上门做饭&#xff0c;上门助餐&#xff0c;生活照护&#xff0c;康复理疗、精神慰藉、委托代办等。各项服务的详情介绍。 2.选择预约时间 选择…

【RabbitMQ】之持久化机制

目录 一、RabbitMQ 持久化机制 1、RabbitMQ 持久化概述2、队列持久化3、消息持久化4、交换器持久化 二、RabbitMQ 知识扩展 1、内存告警与内存换页2、磁盘告警与配置3、数据写入磁盘时机4、磁盘消息格式5、磁盘文件删除机制 一、RabbitMQ 持久化机制 1、RabbitMQ 持久化概述…

2023-07-31:用r、e、d三种字符,拼出一个回文子串数量等于x的字符串。 1 <= x <= 10^5。 来自百度。

2023-07-31&#xff1a;用r、e、d三种字符&#xff0c;拼出一个回文子串数量等于x的字符串。 1 < x < 10^5。 来自百度。 答案2023-07-31&#xff1a; 大体步骤如下&#xff1a; 1.初始化一个字符串builder&#xff0c;用于构建结果字符串。 2.初始化一个字符变量c…

剑指offer10-I.斐波那契数列

学计算机的对这道题肯定不陌生&#xff0c;我记得是学C语言的时候学递归的时候有这道题&#xff0c;于是我就世界用递归写了如下代码&#xff1a; class Solution {public int fib(int n) {if(n1) return 1;if(n0) return 0;return (fib(n-1) fib(n-2)) % 1000000007;} } 到…

ubuntu22.0安装Barrier局域网共享鼠标键盘

ubuntu22.0安装Barrier局域网共享鼠标键盘 参考网站安装步骤客户端一直开启中解决 参考网站 https://idroot.us/install-barrier-ubuntu-22-04/ 安装步骤 sudo apt update sudo apt upgrade sudo apt install wget apt-transport-https gnupg2 software-properties-common s…

rdb和aof

RDB持久化&#xff1a;原理是将Redis在内存中的数据库记录定时dump到磁盘上的RDB持久化AOF持久化&#xff1a;原理是将Redis的操作日志以追加的方式写入文件 rdb&#xff1a; 开启方式&#xff1a;客户端可以通过向Redis服务器发送save或bgsave命令让服务器生成rdb文件&#…

【Linux】更换jdk版本

目录 一、前言二、查看jdk版本号1、项目中的版本号&#xff08;pom.xml&#xff09;2、服务器中的版本号 三、更换jdk版本1、创建java文件夹2、下载并解压JDK安装包①、下载jdk安装包②、移动到创建好的/usr/local/java路径下③、解压jdk安装包 四、删除原来的jdk版本1、删除原…

企业知识文档管理+群晖nas安全云存储

企业知识管理系统&#xff0c;利用软件系统或其他工具的企业管理方法&#xff0c;利用软件系统或其他工具&#xff0c;对组织中大量的有价值的方案、策划、成果、经验等知识进行分类存储和管理&#xff0c;积累知识资产避免流失&#xff0c;促进知识的学习、共享、培训、再利用…