封装Redis工具类

基于StringRedisTemplate封装一个缓存工具类,满足以下需求:

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

方法1,方法3对应是普通的缓存,解决缓存穿透,设置TTL
方法2,方法4结合起来是解决热点key,解决击穿问题.

在这里插入图片描述

package com.hmdp.utils;import lombok.Data;import java.time.LocalDateTime;@Data
public class RedisData {private LocalDateTime expireTime;//逻辑过期时间private Object data;//万能存储数据的对象 对原来实体不需要修改 也可以使用继承
}
package com.hmdp.utils;import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;
import static com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY;@Slf4j
@Component //将来这个bean由Spring维护
public class CacheClient {private final StringRedisTemplate stringRedisTemplate;public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}//方法1public void set(String key, Object value, Long time, TimeUnit unit){stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);}//方法2public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){// 设置逻辑过期RedisData redisData = new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//写入redisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}//方法三public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R>type, Function<ID,R> dbFallback,Long time, TimeUnit unit){String key = keyPrefix + id;//查询缓存String json = stringRedisTemplate.opsForValue().get(key);//判断是否存在if(StrUtil.isNotBlank(json)){//存在,直接返回return JSONUtil.toBean(json,type);}//判断是否为空值if(json!=null){//返回一个错误信息return null;}//不存在,根据id查询数据库//传入函数有参有返回值的函数Function 参数类型ID 返回值类型R//函数式编程 就是定义的时候参数传了个接口,用到时传入接口的实现类R r = dbFallback.apply(id);//不存在,返回错误if(r==null){//将空值写入redisstringRedisTemplate.opsForValue().set(key,"",time,unit);//返回错误信息return null;}this.set(key,r,time,unit);return r;}//线程池private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);public <R,ID> R queryWithLogicalExpire(String keyPrefix,ID id,Class<R>type,Function<ID,R>dbFallback,Long time, TimeUnit unit){String key = keyPrefix + id;//1.从redis查询商铺缓存  也可以用springcacheString json = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isBlank(json)) {//3.存在,直接返回return null;}//4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(json, RedisData.class);JSONObject data = (JSONObject) redisData.getData();R r = JSONUtil.toBean(data,type);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1 未过期 直接返回店铺信息return r;}//5.2 已过期,需要缓存重建//6.重建缓存//6.1 获取互斥锁String lockKey = LOCK_SHOP_KEY + id;boolean isLock = tryLock(lockKey);//6.2 判断是否获取锁成功//注意: 获取锁成功应该再次检测redis缓存是否过期,做DoubleCheck.如果存在则无需重建缓存if(isLock){//6.3 成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(() ->{//查询数据库R apply = dbFallback.apply(id);//写入redisthis.setWithLogicalExpire(key,apply,time,unit);});}//6.4 返回过期商铺信息return r;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key,"1",10,TimeUnit.SECONDS);//需要转化为基本类型返回 如果直接return flag是有可能发生拆箱的,可能出现空指针return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}}

应用:

@Resourceprivate CacheClient cacheClient;@Overridepublic Result queryById(Long id) {//缓存穿透//Shop shop = queryWIthPassThrough(id);
//        id2 -> getById(id2) == this::getById
//        Shop shop = cacheClient.
//                queryWithPassThrough(CACHE_SHOP_KEY,id,Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);//互斥锁解决缓存击穿
//        Shop shop = queryWithMutex(id);//逻辑过期解决缓存击穿
//        Shop shop = queryWithLogicalExpire(id);Shop shop =cacheClient.queryWithLogicalExpire(CACHE_SHOP_KEY,id, Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);if (shop==null) {return Result.fail("店铺不存在!");}return Result.ok(shop);}

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

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

相关文章

三电平空间矢量详解

0. 前言 空间矢量PWM控制策略是依据变流器空间电压切换来控制变流器的一种新颖思路的控制策略。最早由日本学者在20世纪80年代初针对交流电动机变频驱动而提出的,主要思路在于抛弃了原有的正弦波脉宽调制,而是采用逆变器空间电压的矢量以获得准圆形旋转磁场,从而在不高的开关…

Go 切片:用法和本质

要想更好的了解一个知识点&#xff0c;实战是最好的经历。 题目 我这里放一道题目&#xff1a; package mainimport "fmt"func SliceRise(s []int) {s append(s, 0)for i : range s {s[i]}fmt.Println(s) }func SlicePrint() {s1 : []int{1, 2}s2 : s1s2 append…

蓝桥杯备考:堆和priority queue(优先级队列)

堆的定义 heap堆是一种特殊的完全二叉树&#xff0c;对于树中的每个结点&#xff0c;如果该结点的权值大于等于孩子结点的权值&#xff0c;就称它为大根堆&#xff0c;小于等于就叫小根堆&#xff0c;如果是大根堆&#xff0c;每个子树也是符合大根堆的特征的&#xff0c;如果是…

快速搭建深度学习环境(Linux:miniconda+pytorch+jupyter notebook)

本文基于服务器端环境展开&#xff0c;使用的虚拟终端为Xshell。 miniconda miniconda是Anaconda的轻量版&#xff0c;仅包含Conda和Python&#xff0c;如果只做深度学习&#xff0c;可使用miniconda。 [注]&#xff1a;Anaconda、Conda与Miniconda Conda&#xff1a;创建和管…

springboot医院信管系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

《自动驾驶与机器人中的SLAM技术》ch8:基于预积分和图优化的紧耦合 LIO 系统

和组合导航一样&#xff0c;也可以通过预积分 IMU 因子加上雷达残差来实现基于预积分和图优化的紧耦合 LIO 系统。一些现代的 Lidar SLAM 系统也采用了这种方式。相比滤波器方法来说&#xff0c;预积分因子可以更方便地整合到现有的优化框架中&#xff0c;从开发到实现都更为便…

微信消息群发(定时群发)-UI自动化产品(基于.Net平台+C#)

整理 | 小耕家的喵大仙 出品 | CSDN&#xff08;ID&#xff1a;lichao19897314&#xff09; 关联源码及工具下载https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/…

ESP8266-01S、手机、STM32连接

1、ESP8266-01S的工作原理 1.1、AP和STA ESP8266-01S为WIFI的透传模块&#xff0c;主要模式如下图&#xff1a; 上节说到&#xff0c;我们需要用到AT固件进行局域网应用&#xff08;ESP8266连接的STM32和手机进行连接&#xff09;。 ESP8266为一个WiFi透传模块&#xff0c;和…

动手学大数据-3社区开源实践

目录 数据库概览&#xff1a; MaxComput&#xff1a; HAWQ&#xff1a; Hologres&#xff1a; TiDB&#xff1a; Spark&#xff1a; ClickHouse&#xff1a; Apache Calcite 概览 Calcite RBO HepPlanner 优化规则&#xff08;Rule&#xff09; 内置有100优化规则 …

WPS数据分析000004

目录 一、表格阅读技巧 冻结窗格 拆分窗口 新建窗口 阅读模式 护眼模式 二、表格打印技巧 打印预览 打印缩放 打印区域 打印标题 分页打印 打印位置 页眉页脚 逐份打印 三、表格保护技巧 锁定单元格 隐藏公式 文档权限 文件加密 一、表格阅读技巧 冻结窗…

【Android】蓝牙电话HFP连接源码分析

一、概述 在Android系统中&#xff0c;HF&#xff08;Hands-Free Profile&#xff09;客户端与AG&#xff08;Audio Gateway&#xff09;端之间的HFP&#xff08;Hands-Free Profile&#xff09;连接是蓝牙音频通信的重要组成部分。这一过程涉及多个层次和组件的协同工作&…

Redisson发布订阅学习

介绍 Redisson 的消息订阅功能遵循 Redis 的发布/订阅模式&#xff0c;该模式包括以下几个核心概念&#xff1a; 发布者&#xff08;Publisher&#xff09;&#xff1a;发送消息到特定频道的客户端。在 Redis 中&#xff0c;这通过 PUBLISH 命令实现。 订阅者&#xff08;Sub…

【Linux 重装】Ubuntu 启动盘 U盘无法被识别,如何处理?

背景 U盘烧录了 Ubuntu 系统作为启动盘&#xff0c;再次插入电脑后无法被识别 解决方案&#xff08;Mac 适用&#xff09; &#xff08;1&#xff09;查找 USB&#xff0c;&#xff08;2&#xff09;格式化&#xff08;1&#xff09;在 terminal 中通过 diskutil list 查看是…

【LLM-RL】DeepSeekMath强化对齐之GRPO算法

note 文章目录 note一、GRPOReference 一、GRPO 论文&#xff1a;DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models &#xff08;https://arxiv.org/pdf/2402.03300&#xff09;GRPO 在 DeepSeek V2 中采用了&#xff0c;GRPO 在训练过程…

Rust Actix Web 项目实战教程 mysql redis swagger:构建用户管理系统

Rust Actix Web 项目实战教程&#xff1a;构建用户管理系统 项目概述 本教程将指导你使用 Rust 和 Actix Web 构建一个完整的用户管理系统&#xff0c;包括数据库交互、Redis 缓存和 Swagger UI 文档。 技术栈 Rust 编程语言Actix Web 框架SQLx (MySQL 数据库)Redis 缓存Uto…

git系列之revert回滚

1. Git 使用cherry-pick“摘樱桃” step 1&#xff1a; 本地切到远程分支&#xff0c;对齐要对齐的base分支&#xff0c;举例子 localmap git pull git reset --hard localmap 对应的commit idstep 2&#xff1a; 执行cherry-pick命令 git cherry-pick abc123这样就会将远程…

Hadoop•用Web UI查看Hadoop状态词频统计

听说这里是目录哦 通过Web UI查看Hadoop运行状态&#x1f407;一、关闭防火墙二、在物理计算机添加集群的IP映射三、启动集群四、进入HDFS的Web UI 词频统计&#x1f9a9;1、准备文本数据2、在HDFS创建目录3、上传文件4、查看文件是否上传成功5、运行MapReduce程序6、查看MapRe…

国产编辑器EverEdit -重复行

1 重复行 1.1 应用场景 在代码或文本编辑过程中&#xff0c; 经常需要快速复制当前行&#xff0c;比如&#xff0c;给对象的多个属性进行赋值。传统的做法是&#xff1a;选中行-> 复制-> 插入新行-> 粘贴&#xff0c;该操作有4个步骤&#xff0c;非常繁琐。 那有没…

LabVIEW桥接传感器数据采集与校准程序

该程序设计用于采集来自桥接传感器的数据&#xff0c;执行必要的设置&#xff08;如桥接配置、信号采集参数、时间与触发设置&#xff09;&#xff0c;并进行适当的标定和偏移校正&#xff0c;最终通过图表呈现采集到的数据信息。程序包括多个模块&#xff0c;用于配置通道、触…

redis-排查命中率降低问题

1.命中率降低带来的问题 高并发系统&#xff0c;当命中率低于平常的的运行情况&#xff0c;或者低于70%时&#xff0c;会产生2个影响。 有大量的请求需要查DB&#xff0c;加大DB的压力&#xff1b;影响redis自身的性能 不同的业务场景&#xff0c;阈值不一样&#xff0c;一般…