redis缓存击穿,redisson分布式锁,redis逻辑过期

什么是缓存击穿:

缓存击穿是指在高并发环境下,某个热点数据的缓存过期,导致大量请求同时访问后端存储系统,引起系统性能下降和后端存储压力过大的现象。

解决方案:
1. redisson分布式锁

本质上是缓存重建的过程中,大量的请求访问到后端的数据库导致数据库压力过大
那么可以使用redisson分布式锁来对缓存重建的过程加锁
其它的线程只有缓存重建完毕之后才可以访问
缺点:所有的请求都要等待拿到锁的线程来进行缓存重建
优点:数据拥有高一致性,适用于某些涉及“钱”的业务,或者要求数据的强一致性的。

  1. 新建redisson子工程单独作为微服务名字叫redisson-starter
  2. 引入redisson相关依赖
        <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.17.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version></dependency>
  1. 项目结构
    在这里插入图片描述
    RedissonConfigProperties:一些redisson需要的配置项,如果是集群此处不能用这种方式
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "redisson-lock")
public class RedissonConfigProperties {private String redisHost;private String redisPort;}

RedissonConfig:配置RedissonClient,并且加入了一些自动装配的配置

import com.yonchao.redisson.service.RedissonLockService;
import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;@Data
@Configuration
@EnableConfigurationProperties({RedissonConfigProperties.class})
//当引入Service接口时
@ConditionalOnClass(RedissonLockService.class)
public class RedissonConfig {@Autowiredprivate RedissonConfigProperties redissonConfigProperties;/*** 对 Redisson 的使用都是通过 RedissonClient 对象* @return*/@Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。public RedissonClient redisson() {// 1.创建配置Config config = new Config();// 集群模式// config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");// 2.根据 Config 创建出 RedissonClient 示例。config.useSingleServer().setAddress("redis://"+redissonConfigProperties.getRedisHost()+":"+redissonConfigProperties.getRedisPort());return Redisson.create(config);}
}

spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.yonchao.redisson.service.RedissonLockService

业务类:

import com.yonchao.redisson.service.RedissonLockService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class RedissonLockServiceImpl implements RedissonLockService {@Autowiredprivate RedissonClient redissonClient;/*** 加锁* @param lockKey* @return*/public boolean acquireLock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);try {return lock.tryLock(30, TimeUnit.SECONDS);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}/*** 释放锁* @param lockKey* @return*/public void releaseLock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.unlock();}
}
  1. 其它微服务通过pom引入redisson-starter微服务
    在这里插入图片描述
  2. 重建缓存过程中使用分布式锁
    首先注入RedissonClient
    接着判断布隆过滤器
    接着从缓存中读数据
    读不到的话需要重建缓存
    重建缓存:
    首先获取分布式锁
    获取成功了就查询数据库并且重建缓存返回数据最后释放锁
    获取分布式锁失败就等待1秒接着递归这个方法,直到有一个线程重建缓存成功。
    /*** 使用逻辑过期的方式* @param id* @return*/@Overridepublic ResponseResult selectArticleLogicalExpiration(Long id) {// 首先经过布隆过滤器// 判断这个id是不是在布隆过滤器中boolean mightContain = bloomFilter.mightContain(id);// 不存在直接返回if (!mightContain) {return ResponseResult.okResult();}// 首先从缓存中获取数据Object articleObj = redisTemplate.opsForValue().get(ARTICLE_KEY + id);if (Objects.nonNull(articleObj)){String articleJSON = (String) articleObj;JSONObject jsonObject = JSON.parseObject(articleJSON);Long expired = jsonObject.getLong("expired");// 旧的文章对象ApArticle article = JSON.parseObject(articleJSON, ApArticle.class);if (Objects.nonNull(expired)){// 未过期直接返回if (expired - System.currentTimeMillis() > 0) {return ResponseResult.okResult(article);}// 过期了进行缓存的重建boolean acquiredLock = redissonLockService.acquireLock(lockKeyRedisson);// 拿到锁了就 新开一个线程 进行缓存的重建 此处使用分布式锁,只会有一个线程抢占到缓存重建所以不用使用线程池if (acquiredLock) {try {new Thread(() -> {// 直接重建缓存,不关心返回值rebuildCache(id);}).start();} finally {// 最后释放锁redissonLockService.releaseLock(lockKeyRedisson);}}// 开启多线程后直接返回旧的数据return ResponseResult.okResult(article);}}// 缓存中根本没有,那么需要直接加锁重建缓存,此时不能多线程的去重建缓存,只能通过分布式锁的方式,boolean lockAcquired = redissonLockService.acquireLock(lockKeyLogicalExpiration);if (lockAcquired){try {ApArticle apArticle = rebuildCache(id);if (Objects.isNull(apArticle)){// 数据不存在就直接返回return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);}// 返回获得的文章数据return ResponseResult.okResult(apArticle);} finally {// 最后释放锁redissonLockService.releaseLock(lockKeyLogicalExpiration);}} else {// 没有获取到锁就等待一段时间然后再次尝试获取锁try {Thread.sleep(1000);}catch (Exception e){log.error(e.getMessage());}// 等待一段时间重新校验有没有缓存return selectArticleLogicalExpiration(id);}}public ApArticle rebuildCache(Long id){// 重建缓存ApArticle articleDatabase = getById(id);// 重建缓存if (Objects.nonNull(articleDatabase)) {// 设置逻辑过期时间// 转为jsonStringString articleJsonString = JSON.toJSONString(articleDatabase);// 转为JSONObjectJSONObject articleJsonObject = JSON.parseObject(articleJsonString);// 当前时间戳加上 设置的过期时间*1000 因为时间戳是毫秒articleJsonObject.put("expired", System.currentTimeMillis() + ARTICLE_EXPIRED * 1000);redisTemplate.opsForValue().set(ARTICLE_KEY + id, JSON.toJSONString(articleJsonObject));// 布隆过滤器过滤过的,这个肯定存在return articleDatabase;}return null;}

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

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

相关文章

数据分析实战 - 2 订单销售数据分析(pandas 进阶)

题目来源&#xff1a;和鲸社区的题目推荐&#xff1a; 刷题源链接&#xff08;用于直接fork运行 https://www.heywhale.com/mw/project/6527b5560259478972ea87ed 刷题准备 请依次运行这部分的代码&#xff08;下方4个代码块&#xff09;&#xff0c;完成刷题前的数据准备 …

pc通过window.open打开新页面,新页面要使用原来页面的token

原文链接&#xff1a; https://blog.csdn.net/weixin_42342065/article/details/127420783 (以下为本人笔记使用) 对于前端来说&#xff0c;一般在登录获取token之后会把token存入缓存以及放置在Request Headers请求头中&#xff0c;但是使用iframe/window.open/a这三种标签打…

J2EE项目部署与发布(Linux版本)->jdktomcat安装,MySQL安装,后端接口部署,linux单体项目前端部署

jdk&tomcat安装MySQL安装后端接口部署linux单体项目前端部署 1.jdk&tomcat安装 上传jdk、tomcat安装包 解压两个工具包 #解压tomcat tar -zxvf apache-tomcat-8.5.20.tar.gz #解压jdk tar -zxvf jdk-8u151-linux-x64.tar.gz 配置并且测试jdk安装 #配置环境变量 vim /e…

Kubernetes包管理工具Helm简介及使用

文章目录 前言技术积累什么是HelmHelm的核心概念Helm可以解决哪些痛点Helm中文官方文档 Helm安装Helm安装nginx用例写在最后 前言 大家都知道K8S是云原生devops的一大利器&#xff0c;可以直接让我们的中间件、应用服务直接运行在云端&#xff0c;让我们可以只关心自身的业务功…

霍尔效应测试系统

霍尔效应是电磁效应的一种&#xff0c;这一现象是美国物理学家霍尔&#xff08;E.H.Hall&#xff0c;1855—1938&#xff09;于1879年在研究金属的导电机制时发现的。当电流垂直于外磁场通过半导体时&#xff0c;载流子发生偏转&#xff0c;垂直于电流和磁场的方向会产生一附加…

Kafka基本原理、生产问题总结及性能优化实践 | 京东云技术团队

Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…

中颖单片机SH367309全套量产PCM,专用动力电池保护板开发资料

方案总体介绍 整套方案硬件部分共2块板子&#xff0c;包括MCU主板&#xff0c;采用SH79F6441-32作为主处理器。MCU主板包括2个版本。PCM动力电池保护板采用SH367309。 软件方案采用Keil51建立的工程&#xff0c;带蓝牙的版本&#xff0c;支持5~16S电池。 硬件方案--MCU主板 MC…

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析 1 题目 2023 年MathorCup 高校数学建模挑战赛——大数据竞赛赛道B&#xff1a;电商零售商家需求预测及库存优化问题电商平台存在着上千个商家&#xff0c;他们会将商品货物放在电商配套的仓库…

信息收集-web架构-源码

一、web架构资产-平台指纹识别-源码 代码审计&#xff0c;从代码中挖掘漏洞&#xff0c;有代码才能做的&#xff0c;没有代码&#xff08;黑盒&#xff09;&#xff0c;有代码&#xff08;白盒&#xff09; 没有源码只能做黑盒&#xff0c;有源码黑白盒都可做 有源码成功率高…

opencv c++ canny 实现 以及与halcon canny的对比

Opencv和C实现canny边缘检测_opencv边缘增强-CSDN博客 一、canny实现步骤 1、图像必须是单通道的&#xff0c;也就是说必须是灰度图像 2、图像进行高斯滤波&#xff0c;去掉噪点 3、sobel 算子过程的实现&#xff0c;计算x y方向 、梯度&#xff08;用不到&#xff0c;但是…

【机器学习】四、计算学习理论

1 基础知识 计算学习理论&#xff08;computational learning theory&#xff09;&#xff1a;关于通过“计算”来进行“学习”的理论&#xff0c;即关于机器学习的理论基础&#xff0c;其目的是分析学习任务的困难本质&#xff0c;为学习算法体统理论保证&#xff0c;并根据结…

如何远程访问具有多个显示器的计算机

留出扩展空间对身体和电脑屏幕都有好处。许多人受益于使用多台显示器或将笔记本电脑连接到外接显示器以扩展屏幕。对于使用屏幕的人来说&#xff0c;拥有这样的屏幕空间可能意味着更高效的工作流程和生产力。 但是&#xff0c;如果你需要远程访问那台计算机呢&#xff1f;是否…