redis-黑马点评-商户查询缓存

缓存:cache

public Result queryById(Long id) {//根据id在redis中查询数据String s = redisTemplate.opsForValue().get(CACHE_SHOP_KEY +id);//判断是否存在if (!StrUtil.isBlank(s)) {//将字符串转为bean//存在,直接返回Shop shop = JSONUtil.toBean(s, Shop.class);return Result.ok(shop);}//不存在,查询数据库Shop shop = getById(id);if (shop==null){//不存在,返回404return Result.fail("店铺不存在");}//数据库中是否存在,存在则写入缓存,并返回redisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop));return Result.ok(shop);}

JSONUtil.toBean(s, Shop.class);

JSONUtil.toJsonStr(shop);

缓存更新策略:

先删除数据库后删除缓存的线程安全可能性低。

缓存穿透:

1.查询店铺在数据库和redis中都不存在时,写入空值到redis中

2.查询数据为空值时,直接返回不要查询数据库。

public Result queryById(Long id) {//根据id在redis中查询数据String s = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY +id);//判断是否存在if (!StrUtil.isBlank(s)) {//将字符串转为bean//存在,直接返回Shop shop = JSONUtil.toBean(s, Shop.class);return Result.ok(shop);}//判断是否店铺是否存在,缓存中的空数据 if (s!=null){//返回空值return Result.fail("店铺信息不存在");}//不存在,查询数据库Shop shop = getById(id);if (shop==null){//不存在,返回404//缓存空值到数据库中stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,"",CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail("店铺不存在");}//数据库中是否存在,存在则写入缓存,并返回stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);return Result.ok(shop);
}

互斥锁解决缓存击穿:

使用redis中的setnx实现互斥锁,如果key不存在则创建,存在则无法创建。

//使用redis中的setnx实现互斥锁。private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);//潜在的NullReferenceException,如果装箱的对象是null,那么在拆箱时可能会抛出NullReferenceExceptionreturn BooleanUtil.isTrue(flag);}private void unLock(String key){stringRedisTemplate.delete(key);}
拆箱和装箱操作可能会产生的问题:

1.性能开销:装箱和拆箱涉及内存分配和复制,这会增加额外的性能开销。

2.类型安全丢失:装箱操作会将值类型包装在一个对象中,这样原本在栈上的值类型数据现在位于堆中,可能导致类型安全检查丢失。

3.垃圾回收压力:装箱操作会创建新的对象实例,这可能增加垃圾回收器的压力,尤其是在频繁进行装箱和拆箱操作的情况下。

4.潜在的NullReferenceException,如果装箱的对象是null,那么在拆箱时可能会抛出NullReferenceException异常。

/*** 互斥锁解决缓存击穿* @param id* @return*/private Shop queryWithMutex(Long id){//根据id在redis中查询数据String s = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY +id);//判断是否存在if (!StrUtil.isBlank(s)) {//将字符串转为bean//存在,直接返回Shop shop = JSONUtil.toBean(s, Shop.class);return shop;}//判断是否店铺是否存在if (s!=null){//返回空值return null;}//缓存重建//尝试获取互斥锁String lockKey= LOCK_SHOP_KEY+id;Shop shop = null;try {boolean isLock = tryLock(lockKey);//判断是否获取锁成功if (!isLock){//获取锁失败,休眠一会重试Thread.sleep(10);return queryWithMutex(id);}//获取锁成功,再次查询缓存中是否存在数据,如果存在,则不需要缓存重建s = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY +id);//判断是否存在if (!StrUtil.isBlank(s)) {//将字符串转为bean//存在,直接返回shop = JSONUtil.toBean(s, Shop.class);unLock(lockKey);return shop;}//判断是否店铺是否存在if (s!=null){//返回空值return null;}//缓存中还是没有数据,查询数据库//不存在,查询数据库shop = getById(id);if (shop==null){//不存在,返回404//缓存空值到数据库中stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,"",CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//数据库中是否存在,存在则写入缓存,并返回stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//释放锁unLock(lockKey);}return shop;}

逻辑过期解决缓存击穿:

创建一个redisData类,维护不同对象的过期时间:

@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}
 /*** 缓存预热将店铺数据存入redis中并且设置逻辑过期时间**/private void save2Redis(Long id,Long expireSeconds){//查询店铺信息Shop shop = getById(id);//封装RedisDataRedisData redisData=new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//保存数据到redis中stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));}
private Shop queryWithLogicExpire(Long id){//根据id在redis中查询数据String s = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY +id);//判断是否存在if (StrUtil.isBlank(s)) {//缓存未命中不存在,直接返回nullreturn null;}//缓存命中,判断缓存是否过期RedisData redisData = JSONUtil.toBean(s, RedisData.class);JSONObject data = (JSONObject) redisData.getData();Shop shop = JSONUtil.toBean(data, Shop.class);LocalDateTime expireTime = redisData.getExpireTime();if (expireTime.isAfter(LocalDateTime.now())){//未过期return shop;}//过期,尝试获取互斥锁String lockKey= LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);if (isLock){//TODO 获取互斥锁成功,重新查询缓存是否过期,如果未过期,不用再缓存重建,如果过期,则重建缓存CACHE_REBUILD_EXECUTOR.submit(()->{try {save2Redis(id,20L);} catch (Exception e) {e.printStackTrace();} finally {unLock(lockKey);}});}//获取互斥锁失败,返回过期店铺信息return shop;}

封装Redis缓存工具类,包含四个方法:


@Component
@Slf4j
public class CacheClient {private final StringRedisTemplate stringRedisTemplate;
//    基于构造函数注入,不太使用,不太理解public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}
//缓存数据到redis中,并设置过期时间public void set(String key, Object value, Long time, TimeUnit unit){stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);}
//缓存数据到redis中,设置逻辑过期时间public void setLogicExpire(String key, Object value, Long time, TimeUnit unit){//设置逻辑过期RedisData redisData=new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//写入redis中stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData),time,unit);}
//缓存穿透解决方案,public  <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time, TimeUnit unit){String key=keyPrefix+id;//根据id在redis中查询数据String json = stringRedisTemplate.opsForValue().get(key);//判断是否存在if (!StrUtil.isBlank(json)) {//将字符串转为bean//存在,直接返回R r= JSONUtil.toBean(json, type);return r;}//判断是否店铺是否存在if (json!=null){//返回空值return null;}//不存在,查询数据库R r = dbFallback.apply(id);if (r==null){//不存在,返回404//缓存空值到数据库中stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//数据库中是否存在,存在则写入缓存,并返回this.set(key,r,time,unit);return r;}
//缓存击穿解决方案:逻辑过期private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);/*** 使用逻辑过期解决缓存击穿* @param id* @return*/public  <R,ID> R queryWithLogicExpire(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time, TimeUnit unit){//根据id在redis中查询数据String key=keyPrefix+id;String json = stringRedisTemplate.opsForValue().get(key);//判断是否存在if (StrUtil.isBlank(json)) {//缓存未命中不存在,直接返回nullreturn null;}//缓存命中,判断缓存是否过期RedisData redisData = JSONUtil.toBean(json, RedisData.class);JSONObject data = (JSONObject) redisData.getData();R r=JSONUtil.toBean(data, type);LocalDateTime expireTime = redisData.getExpireTime();if (expireTime.isAfter(LocalDateTime.now())){//未过期return r;}//过期,尝试获取互斥锁String lockKey= LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);if (isLock){//TODO 获取互斥锁成功,重新查询缓存是否过期,如果未过期,不用再缓存重建,如果过期,则重建缓存CACHE_REBUILD_EXECUTOR.submit(()->{try {//重建缓存//查询数据库R r1= dbFallback.apply(id);//缓存数据this.setLogicExpire(key,r1,time,unit);} catch (Exception e) {e.printStackTrace();} finally {unLock(lockKey);}});}//获取互斥锁失败,返回过期店铺信息return r;}//使用redis中的setnx实现互斥锁。private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);//潜在的NullReferenceException,如果装箱的对象是null,那么在拆箱时可能会抛出NullReferenceExceptionreturn BooleanUtil.isTrue(flag);}private void unLock(String key){stringRedisTemplate.delete(key);}
}
总结:

Spring中bean的注入方式:

在Spring框架中,注入Bean(对象)的方式有多种,以下是一些常见的方法:

1. 构造器注入(Constructor Injection):
通过构造器注入依赖,可以确保在创建对象时,它所依赖的其他对象也被创建。

```
public class ExampleBean {
private final AnotherBean anotherBean;

@Autowired
public ExampleBean(AnotherBean anotherBean) {
this.anotherBean = anotherBean;
}
}
```

2. Setter方法注入(Setter Injection):
通过setter方法注入依赖,可以在对象创建后,再设置依赖的对象。

```java
public class ExampleBean {
private AnotherBean anotherBean;

@Autowired
public void setAnotherBean(AnotherBean anotherBean) {
this.anotherBean = anotherBean;
}
}
```

3. 字段注入(Field Injection):
通过字段注入依赖,直接在字段上使用`@Autowired`注解。

```java
public class ExampleBean {
@Autowired
private AnotherBean anotherBean;
}
```

4. 方法参数注入(Method Parameter Injection):
在方法参数上使用`@Autowired`注解,Spring会注入对应的依赖。

```java
public class ExampleBean {
public void doSomething(@Autowired AnotherBean anotherBean) {
// ...
}
}
```

5. 接口注入(Interface Injection):
通过定义一个接口来标记需要注入的Bean。

```java
public interface InjectedInterface {
void injectDependency(AnotherBean anotherBean);
}

public class ExampleBean implements InjectedInterface {
private AnotherBean anotherBean;

@Override
public void injectDependency(AnotherBean anotherBean) {
this.anotherBean = anotherBean;
}
}
```

6. 基于注解的注入(Annotation-based Injection):
使用`@Autowired`、`@Resource`、`@Inject`等注解来标记需要注入的依赖。

```java
public class ExampleBean {
@Autowired
private AnotherBean anotherBean;
}
```

7. 自动装配(Autowiring):
Spring可以自动装配依赖,无需显式注入。

```java
public class ExampleBean {
@Autowired
private AnotherBean anotherBean;
}
```

8. 基于Java配置的注入:
使用`@Configuration`和`@Bean`注解来定义和注入Bean。

```java
@Configuration
public class AppConfig {
@Bean
public ExampleBean exampleBean() {
return new ExampleBean(anotherBean());
}

@Bean
public AnotherBean anotherBean() {
return new AnotherBean();
}
}
```

选择哪种注入方式取决于你的具体需求和设计偏好。构造器注入和setter注入是最常用的方式,因为它们可以保证依赖的完整性和初始化的一致性。而字段注入和基于注解的注

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

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

相关文章

laravel(源码笔记)服务绑定和解析(依赖注入-反射,控制反转)

依赖注入&#xff1a;解决对象之间的互相依赖关系&#xff0c;通过反射实现。控制反转&#xff1a;让外部来定义具体行为 反射&#xff08;基本使用&#xff09; reflectionClass getConstructor()getParameters()getConstants()getProperties()getMethods()newInstanceArgs(…

专家解读!IMAP的要点助您在旅途中保持邮件无忧!

你是否经常因会议而出差&#xff0c;需要在各种设备上灵活地访问你的电子邮件&#xff1f;如果是的话&#xff0c;你可能会想了解你的电子邮件系统是如何通过使用互联网消息访问协议&#xff08;IMAP&#xff09;来工作的&#xff0c;这样当你不在办公桌前时&#xff0c;你可以…

_.debounce防抖函数 在vue中使用this问题,应该传匿名函数而不是箭头函数

简单理解&#xff1a;_.debounce内部做了apply操作&#xff0c;箭头函数由于没有this,无法绑定this,导致最终this是undefined, 而匿名函数&#xff0c;成功通过applay绑定了this,所以this指向了vue组件实例。 methods: {// 防抖动dSave1: _.debounce(() > {console.log(thi…

用汇编进行字符串匹配

用汇编进行字符串匹配 2、试编写一程序&#xff0c;要求比较两个字符串 STRING1 和 STRING2 所含字符是否完全相同&#xff0c;若相同则显示 MATCH&#xff0c;若不相同则显示 NO MATCH。 .model small .dataSTRING1 db hello world!,0STRING2 db hello china!,0matchString d…

你知道弧幕影院如何制作吗?其应用领域竟如此广泛!

“沉浸式”作为如今备受热议的内容展示形式&#xff0c;其有着多种可实现的途径&#xff0c;其中弧幕影院作为一项有着独特视觉效果、沉浸式观影体验的技术类型&#xff0c;便是大多数影院、主题公园等娱乐场景的必备设计展项&#xff0c;这种弧幕影院通常使用大型的半圆形屏幕…

React-创建虚拟Dom四种方法

1.声明div const Son1<div>我言秋日胜春招</div> 2.声明函数 function Son() {return <div>自古逢秋多寂寥</div>;} 3.createElement方法 说明&#xff1a;React.createElement: 这是 React 提供的用于创建元素的函数。它接受三个参数&#xff1a…

python 爬取杭州小区挂牌均价

下载chrome驱动 通过chrome浏览器的 设置-帮助-关于Google Chrome 查看你所使用的Chrome版本 驱动可以从这两个地方找: 【推荐】https://storage.googleapis.com/chrome-for-testing-publichttp://npm.taobao.org/mirrors/chromedriver import zipfile import os import r…

leetcode 232.用栈实现队列 JAVA

题目 思路 使用两个栈&#xff08;输入栈和输出栈&#xff09;来模拟一个队列。 队列的push操作实现&#xff1a;直接将元素push到输入栈中。 队列的pop操作实现&#xff1a;队列是先入先出&#xff0c;将输入栈的元素全部pop到输出栈中&#xff0c;然后再由输出栈pop&#…

PMP备考时间、出成绩时间有多久?从在威班培训到拿证我用了60天

尽管PMI官方没有对PMP考试通过分数进行具体规定&#xff0c;能否通过也是看成绩页显示的是“PASS”&#xff08;通过&#xff09;还是“FAIL”&#xff08;未通过&#xff09;&#xff0c;没有成绩的数值体现&#xff0c;但有每个领域的等级可以进行查看&#xff0c;比如下图。…

并网型风光储微电网日前优化调度(MATLAB实现)

考虑了光伏发电、风力发电、电池储能和负荷需求等因素&#xff0c;与主网相连不考虑向主网售电情况。 % 微电网日前优化调度示例代码% 定义时间步长&#xff08;例如&#xff0c;每小时&#xff09; time_steps 24;% 生成模拟数据&#xff1a;光伏发电量&#xff0c;风力发电…

Windows系统服务器宝塔面板打开提示Internal Server Error错误

1、cmd运行bt命令 2、尝试输入16修复程序 3、如果不行&#xff0c;输入17升级程序

STL —— string(1)

目录 1. 模板 1.1 泛型编程 1.2 函数模板 1.2.1 函数模板概念 1.2.2 函数模板格式 1.2.3 函数模板的原理 1.2.4 显式实例化 1.2.5 模板参数的匹配原则 1.3 类模板 1.3.1 类模板定义格式 1.3.2 类模板的实例化 2. STL —— string类 2.1 STL 简介 2.2 标准库中的s…

微服务篇-C 深入理解第一代微服务(SpringCloud)_IV 深入理解Hystrix断路器

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载 Part 1 理论部分 1 什么是微服务系统中的高可用&#xff1f; 在复杂的大型微服务系统中&#xff0c;高可用相关的技术方案非常重要&#x…

怎样隐藏查询和分组?

发布查询时&#xff0c;遇到信息量较大需要提前制作好&#xff0c;但不用马上发布的查询&#xff0c;该怎样隐藏查询和分组&#xff1f; &#x1f4cc;使用教程 01“开始”和“暂停”查询 如果想要隐藏查询&#xff0c;可以通过点击绿色开始按钮来暂停查询&#xff0c;暂停后的…

C++多态实现原理深度解析

C多态实现原理深度解析 目录 C多态实现原理深度解析 一、引言&#xff1a;多态性的基本概念与重要性 二、虚函数表&#xff08;VTable&#xff09;与虚函数指针&#xff08;VPtr&#xff09; 三、动态绑定与静态绑定 四、纯虚函数与抽象基类 五、继承与多态的关系 六、多…

【软考高项】十五、信息系统工程之系统集成

1、集成基础 定义&#xff1a;通过硬件平台、网络通信平台、数据库平台、工具平台、应用软件平台将各类资源有机、高效地集成到一起&#xff0c;形成一个完整的工作台面 基本原则包括:开放性、结构化、先进性和主流化 2、网络集成 包括&#xff1a;传输子系统、交换子系统、…

调试西门子G120STO模式出现O.F1600等一系列报警

目录 一、现象描述 二、 解决经历 三、结果展示 四、总结 一、现象描述 在调试使用西门子G120的STO功能时&#xff0c;一直无法使用&#xff0c;变频器也一直在报警(RDY灯红灯快闪、SAFE灯黄灯快闪)。在博图上查询发现下面一系列的故障报警。 二、 解决经历 也查询了很多网…

Vue中的状态管理Vuex,基本使用

1.什么是Vuex? Vuex是专门为Vue.js设计的状态管理模式;特点:集中式存储和管理应用程序中所有组件状态,保证状态以一种可预测的方式发生变化。 1.1.什么是状态管理模式? 先看一个单向数据流的简单示意图 state:驱动应用的数据源 view:以声明方式将state映射到视图 actions:…

实施业务连续性方案

实施业务连续性方案&#xff0c;可以提高组织在面对突发事件和业务中断时的韧性和恢复能力&#xff0c;保障业务的持续运行和服务的可用性。请注意&#xff0c;具体的方案内容应根据组织的特点、业务需求和风险状况进行定制化。 一、目标与范围 确定业务连续性方案的目标&#…

导入GPG密钥的方法

在配置Docker时&#xff0c;发现Docker官方和阿里云导入gpg秘钥的方法不同&#xff0c;借此记录。 Docker官方 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 优点: 安全性: 这种方法不会直接将密钥添加到APT的密…