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

news2024/11/16 18:08:31
什么是缓存击穿:

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

解决方案:
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 {


    @Autowired
    private 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 {
    @Autowired
    private 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
     */
    @Override
    public 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)) {
            // 设置逻辑过期时间
            // 转为jsonString
            String articleJsonString = JSON.toJSONString(articleDatabase);
            // 转为JSONObject
            JSONObject 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.coloradmin.cn/o/1151029.html

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

相关文章

echarts的legend图例,要给图例中的不同文字设置不同颜色

可以用rich&#xff0c;先创建样式a&#xff0c;然后在formatter中用{a|文字}的形式使用&#xff0c;就能将文字使用a样式了

什么是全排列?(算法实现)

全排列是什么&#xff1f; 全排列是指将一组元素按照一定顺序进行排列的所有可能结果。以一组数字为例&#xff0c;比如[1, 2, 3]的全排列结果为&#xff1a;[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]。 全排列有许多不同的计算方法&#xff0c;其中…

mybatis-plus正确使用姿势:依赖配置、Mapper扫描、多数据源、自动填充、逻辑删除。。。

一、前言 本文基于 springboot、maven、jdk1.8、mysql 开发&#xff0c;所以开始前我们需要准备好这套环境。 1.1 依赖准备 想要什么依赖版本的去 maven 仓库查看&#xff1a;https://mvnrepository.com/ 引入 mybatis-plus 依赖&#xff1a; <dependency><group…

【Linux】冯诺依曼体系结构以及初始操作系统

文章目录 冯诺依曼体系结构操作系统概念设计OS的目的定位如何理解管理 总结系统调用和库函数概念 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识…

黑豹程序员-架构师学习路线图-百科:jMeter并发测试计划

我们开发一个软件系统&#xff0c;为了保证代码的正确&#xff0c;我们需要测试。测试日常包括&#xff1a;单元测试、功能测试、集成测试、压力测试、回归测试。 Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具&#xff0c;用于对软件做压力测试。 JMeter 最初被…

JAVA反射机制及动态代理

反射机制 反射机制是什么 1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息&#xff0c;从而操作类或对象的属性和方法。本质是JVM得到class对象之后&#xff0c; 再通过class对象进行反编译&#xff0c;从而获取对象的各种信息。 2、Java属于先编译再运行的…

Flutter 使用 GetX 中遇到的问题

创建了控制器&#xff0c;但是在别的页面中&#xff0c;无法引用控制器里面的某些变量 如下图&#xff1a;后来发现&#xff0c;是命名的问题&#xff0c; 如果是以 _ 下划线开头的变量&#xff0c;那么就无法被引用

(三)docker:Dockerfile构建容器运行jar包

目录结构以及准备的文件 ├── dockerfile │ ├── Dockerfile │ ├── application.properties │ ├── demo.jar │ └── jdk-17.0.9-linux-x64.tar.gz2.Dockerfile内容 FROM ubuntu:latest # JDK存放处 ENV JAVA_DIR/home # 拷贝本地jdk到容器home目录下…

京东数据分析:2023年9月京东洗地机行业品牌销售排行榜

鲸参谋监测的京东平台9月份洗地机市场销售数据已出炉&#xff01; 9月份&#xff0c;洗地机市场的销售额增长。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;9月京东平台上洗地机的销量为9.2万&#xff0c;销售额将近2.2亿&#xff0c;同比增长约9%。从价格上看&#…

如何使用htmltab库

htmltab是一个用于从HTML表格中提取数据的Python库。它可以将HTML表格转换为Pandas数据框&#xff0c;方便进行数据处理和分析。 要使用htmltab库&#xff0c;首先需要安装htmltab。可以使用pip命令来安装htmltab&#xff0c;命令如下&#xff1a; pip install htmltab 安装完…

[SHCTF 2023 校外赛道] pwn

有19道题这么多,不过基本是入门题,都是在骗新生,看这么容易快来PWN吧! week1 四则计算器 这里用危险函数gets读入有个溢出.而且PIE也没开,地址是固定的.而且有后门.直接溢出到ret写上后门即可. from pwn import *p remote(112.6.51.212, 31473) context(archamd64, log_lev…

【手机端远程连接服务器】安装和配置cpolar+JuiceSSH:实现手机端远程连接服务器

文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …

Redis(10)| I/O多路复用(mutiplexing)

上文select/epoll 在上文《Redis&#xff08;09&#xff09;| Reactor模式》 思考问题可以使用I/O多路复用技术解决多多客户端TCP连接问题&#xff0c;同时也提到为了解决最早期的UNIX系统select调用存在的四个问题。 select(int nfds, fd_set *r, fd_set *w, fd_set *e, stru…

数据库拓展语句,约束方式和用户管理

拓展语句 删除表内的所有数据 delete truncate drop 1.delete from test&#xff1b; delete删除是一行一行删除&#xff0c;如果表中有自增长列&#xff0c;清空所有记录之后&#xff0c;再次添加内容&#xff0c;会从原来的记录之后继续自增写入 2.truncate table test;…

docker 安装minio,访问地址进不去

文章目录 黑马头条P37docker安装minio文图一、启动后页面一直是加载状态进不去 黑马头条P37docker安装minio文图 一、启动后页面一直是加载状态进不去 通过docker logs -f (容器id)查看日志 通过这个报错信息&#xff0c;得知最近minio 升级&#xff0c;一些启动信息和之前不…

全球运营的游戏公司,实现存储就近访问、提升访问安全和效率

在上一篇文章&#xff08;永远在线的网游公司&#xff0c;如何在线替换开源存储&#xff1f; &#xff09;中&#xff0c;我们介绍了 XSKY星辰天合如何协助游戏公司解决在存储系统建设中遇到的挑战&#xff0c;这家游戏公司需要一直在线&#xff0c;以便为客户提供服务。 通过…

sass相关

1、代码简化 1.1、简化媒介查询 mixin flex{display: flex;justify-content: center;align-items: center; } .header{width: 100%;include flex;//可以这样引用 }//加入参数 mixin flex($layout){display: flex;justify-content: $layout;align-items: $layout; } .header{w…

行情分析——加密货币市场大盘走势(10.30)

目前大饼开始了震荡盘整&#xff0c;可以在这个位置33000-35000短线逢低做多。大饼依然以多头为主&#xff0c;少做空单。目前震荡行情&#xff0c;一直也跌不下去&#xff0c;等待行情到来即可。 目前开始震荡&#xff0c;也是修复指标&#xff0c;现在大饼的价格远离EMA21均线…

【斗破年番】暗杀行动开始,萧炎斩杀负伤,彩鳞心疼霸气回击

【侵权联系删除】【文/郑尔巴金】 深度爆料&#xff0c;《斗破苍穹》年番第69集刚刚更新了&#xff01;在这集剧情中&#xff0c;萧炎和美杜莎筹划了一场暗杀行动&#xff0c;以保障炎盟的安全。他们根据小医仙提供的地图&#xff0c;分别负责击杀慕兰三老和雁落天这两位敌方强…

云服务器安装Hbase

文章目录 1. HBase安装部署2.HBase服务的启动3.HBase部署高可用&#xff08;可选&#xff09;4. HBase整合Phoenix4.1 安装Phoenix4.2 **Phoenix Shell** 操作4.3 表的映射4.4 Phoenix二级索引4.4.1 全局索引&#xff08;global index&#xff09;4.4.2 包含索引(covered index…