【系统开发】尚硅谷 - 谷粒商城项目笔记(五):分布式缓存

news2024/11/26 16:39:33

文章目录

  • 分布式缓存
    • 缓存使用场景
    • redis作缓存中间件
      • 引入redis依赖
      • 配置redis
      • 堆外内存溢出
    • 缓存失效问题
      • 缓存穿透
      • 缓存雪崩
      • 缓存击穿
    • Redisson分布式锁
      • 导入依赖
      • redisson配置类
      • 可重入锁
      • 读写锁
      • 缓存一致性解决
    • 缓存-SpringCache
      • 简介
      • @Cacheable
      • 自定义缓存配置
      • @CacheEvict
      • @CachePut
      • 原理与不足


分布式缓存

缓存使用场景

image-20220208135426837

image-20220208135510086

image-20220208135906136

redis作缓存中间件

引入redis依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置redis

# ip地址
spring.redis.host=124.222.43.217
# 端口
spring.redis.port=6379

堆外内存溢出

image-20220208142445893

<!--引入redis,排除lettuce,使用jedis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

缓存失效问题

缓存穿透

image-20220208142558424

缓存雪崩

image-20220208142803867

缓存击穿

image-20220208142858386

Redisson分布式锁

导入依赖

<!-- 以后使用redisson作为所有分布式锁,分布式对象等功能框架 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

redisson配置类

@Configuration
public class MyRedissonConfig {
    @Bean(destroyMethod="shutdown")
    RedissonClient redissonClient() throws IOException {
        Config config = new Config();
        // 这里必须以redis://开头,否则会报错
        config.useSingleServer().setAddress("redis://124.222.43.217:6379");
        return Redisson.create(config);
    }
}

可重入锁

可重入锁解释:无论是公平方式还是非公平方式,进门坐下来之后,你可以问医生问题一次,两次,无数次( 重入),只要你还坐着,你都可以问,但是一旦起身离开座位,你的位置就会被抢,除非没人排队,不然你失去了提问的资格。

@ResponseBody
@GetMapping("/hello")
public String hello() {
    //1 获取一把锁,只要锁的名字一样,就是同一把锁
    RLock lock = redisson.getLock("my-lock");
    // 2 加锁
    lock.lock(); // 阻塞式等待,默认加的锁都是30s
    // 锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s。不用担心业务时间长,锁自动过期被删除
    // 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除
    lock.lock(10, TimeUnit.SECONDS); //10s自动解锁,自动解锁时间一定要大于业务的执行时间
    // lock.lock(10, TimeUnit.SECONDS); // 锁时间到了之后,不会自动续期
    try {
        System.out.println("加锁成功,执行业务" + Thread.currentThread().getId());
        // 模拟长业务5s
        Thread.sleep(5000);
    } catch (Exception e) {

    }finally {
        // 3 解锁
        System.out.println("释放锁" + Thread.currentThread().getId());
        lock.unlock();
    }
    return "hello";
}

读写锁

读锁(共享锁)会等待写锁(互斥锁,排他锁)释放,保证一定能读到最新数据

写锁也会等待读锁释放,保证写数据时不能读取数据

总结:读写互斥,不管是先读后写,还是先写后读,都会进行阻塞等待

    @GetMapping("/write")
    @ResponseBody
    public String writeValue() {
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        // 改数据,加写锁
        RLock rLock = lock.writeLock();
        try {
            rLock.lock();
            System.out.println("nihao");
            s = UUID.randomUUID().toString();
            Thread.sleep(30000);
            redisTemplate.opsForValue().set("writeValue", s);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rLock.unlock();
        }
        return s;
    }

    @GetMapping("/read")
    @ResponseBody
    public String readValue() {
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        // 读数据,加读锁
        RLock rLock = lock.readLock();
        try {
            rLock.lock();
            s = redisTemplate.opsForValue().get("writeValue");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rLock.unlock();
        }
        return s;
    }

缓存一致性解决

双写模式:写操作后,同时修改缓存

失效模式:写操作后,删除缓存

image-20220208161958708

image-20220208162404365

image-20220208162549958

缓存-SpringCache

简介

image-20220208182042271

image-20220208181958192

image-20220208182936391

@Cacheable

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

properties配置

# 使用redis作为缓存
spring.cache.type=redis
# 设置有效时间,毫秒为单位,3600*1000
spring.cache.redis.time-to-live=3600000
# 是否使用缓存前缀
spring.cache.redis.use-key-prefix=true
# 指定缓存前缀,如果不指定,则默认使用注解中value指向的值(分组名),如果value没有指向任何值,则无缓存前缀
# spring.cache.redis.key-prefix=WSKH_CACHE_
# 是否缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true
spring.session.store-type=redis

启动类上加注解,开启缓存

@EnableCaching

在要开启缓存的方法上加上@Cacheable注解,示例如下所示:

// 每一个需要缓存的数据我们都要指定放到哪个名字的缓存。【缓存的分区】
// 当前方法的结果需要缓存,如果缓存中有,方法不调用,如果缓存中没有,会调用方法,最后将方法的结果放入缓存
// value = {"category"} 表示:属于哪个缓存分区(分组),当没有指定缓存前缀的时候,就会使用这个分组名作为前缀
// key = "#root.method.name" 表示:将方法名作为存入redis中的键
// sync = true 表示:是否为同步代码块
@Cacheable(value = {"category"},key = "#root.method.name",sync = true)  
@Override
public List<CategoryEntity> getLevel1Categorys() {
    long l = System.currentTimeMillis();
    List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    System.out.println("消耗时间," + (System.currentTimeMillis() - 1));
    return categoryEntities;
}

自定义缓存配置

默认缓存数据是保存JDK序列化后的数据,一般业务上需要使用JSON格式进行缓存的存储(JSON具有跨语言,跨平台的高兼容性)

@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class MyCacheConfig {
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

@CacheEvict

  • value = “category”:指定分组名,即缓存默认前缀
  • allEntries = true:指定删除value指向的分组下的所有缓存数据
  • key = " ‘myKey’ ":指定缓存的key值,如果是普通字符串,则需要在双引号中加单引号,将其包裹
@CacheEvict(value = "category",allEntries = true)  // 失效模式
@CachePut // 双写模式
@Transactional
@Override
public void updateCasecade(CategoryEntity category) {
    this.updateById(category);
    categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
}

同时清除多个缓存

image-20220208190615505

@CachePut

双写模式,修改完数据库后,将返回的结果更新到缓存中,如果方法返回修饰符为void,那么就不能使用@CachePut注解

原理与不足

image-20220208191257439

image-20220208191733676

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

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

相关文章

【网络】协议的定制与Json序列化和反序列化

文章目录 应用层初识TCP协议通讯流程定制协议再谈协议网络版本计算器Protocal.hppCalServerCalClient Json的安装 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 初识TCP协议通讯流程 建立链接和断开链接 基于TCP协议&#xff0c…

六、使用深度学习构建人脸识别模型

本章介绍机器学习中人脸识别的历史以及从零开始如何构建一个人脸识别模型,含所有训练数据,源代码,不强制要求GPU。使用 docker 来管理库依赖项,提供与平台无关的一致环境。使用 Dlib 进行预处理,使用 Tensorflow + Scikit-learn 训练能够根据图像预测身份的分类器。 1、人…

《吉林省教育学院学报》简介及投稿邮箱

《吉林省教育学院学报》简介&#xff1a; 主管单位 吉林省教育厅 主办单位 吉林省教育学院 出版周期&#xff1a;月刊 国际刊号&#xff1a;ISSN&#xff1a;1671-1580&#xff1b;国内刊号&#xff1a;CN&#xff1a;22-1296/G4&#xff1b;邮发代号&#xff1a;12-223 出…

创建线程三种方法

创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() { public void run() {// 要执行的任务} }; // 启动线程 t.start(); 例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐Thread t1 new Thread("t1"…

Doris的安装

Doris的安装 文章目录 Doris的安装写在前面Linux 操作系统版本需求软件需求操作系统安装要求设置系统最大打开文件句柄数时钟同步关闭交换分区&#xff08;swap&#xff09; 开发测试环境生产环境 安装下载安装包默认端口集群部署前置准备安装部署FE安装部署BE在 **FE** 中添加…

2.1C++派生

C派生概述 C中的派生允许从一个已有的类中创建一个新的类&#xff0c;该新类继承了原有类的属性和方法。 派生类可以增加新的属性和方法&#xff0c;也可以重写原有类的方法以改变其行为。 C中的派生类可以通过公有、私有和保护继承来继承基类的成员。 公有继承允许派生类访…

网络协议驱动互联网

在分布式系统中&#xff0c;数据通过各种网络协议在网络中传输。作为应用程序开发者&#xff0c;这往往在问题出现之前似乎是一个黑盒子。 在本文中&#xff0c;我们将解释常见网络协议的工作原理&#xff0c;它们在分布式系统中的应用以及我们如何解决常见问题。后续还会介绍一…

开源一键拥有你自己的ChatGPT+Midjourney网页服务,用不用是另一回事,先收藏!

功能支持 原ChatGPT-Next-Web所有功能 midjourney imgine 想象 midjourney upscale 放大 midjourney variation 变幻 midjourney describe 识图 midjourney blend 混图 midjourney 垫图 绘图进度百分比、实时图像显示 自身支持midjourney-api 参数说明 MIDJOURNEY_PROXY_URL …

组态王与多台PLC之间无线以太网通信

在实际系统中&#xff0c;同一个车间里分布多台PLC&#xff0c;通过上位机集中控制。通常所有设备距离在几十米到上百米不等。在有通讯需求的时候&#xff0c;如果布线的话&#xff0c;工程量较大耽误工期&#xff0c;这种情况下比较适合采用无线通信方式。 本方案以组态王和2…

java -D详解

官方文档对 -D 有明确的解释&#xff0c;具体看 https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html 简单解释一下 首先&#xff0c;java 命令是这么用的 其次&#xff0c;-D 是属于 [options] 这一块的。而 [options] 又分为如下几类 -D 就属于标准选…

企业级医疗项目:码猿慢病云管理系统来了!

大家好&#xff0c;我是不才陈某~ 不知不觉转行到医疗领域已经两年多了&#xff0c;前后服务过三百多家医疗机构&#xff0c;深耕于医疗领域&#xff0c;不知道在座的各位有从事医疗领域工作的吗&#xff1f; 上个月和朋友搞了一套的企业级的医疗实战项目&#xff1a;码猿慢病云…

Arthas线上故障案例分析——内存使用率上升,负载突然变高

使用经验分享 线上故障排查思路&#xff1a; 1、紧急处理&#xff0c;优先保障服务可用&#xff08;如切换vip&#xff0c;主备容灾&#xff09; 2、保留第一现场&#xff0c;通过jstack -l {pid} > jvmtmp.txt &#xff0c;打印栈信息 &#xff08;后续可以在gceasy官网上…

Maven插件开发及Demo演示

引言 maven不仅仅只是项目的依赖管理工具&#xff0c;其强大的核心来源自丰富的插件&#xff0c;可以说插件才是maven工具的灵魂。本篇文章将对如何自定义maven插件进行讲解&#xff0c;希望对大家有所帮助。 背景 讲如何开发maven插件之前&#xff0c;不妨先来聊一下什么是…

STM32速成笔记—定时器

文章目录 一、什么是定时器二、定时器有什么用三、通用定时器详细介绍3.1 时钟来源3.2 预分频器&#xff0c;计数器&#xff0c;自动重装载寄存器3.2.1 预分频器3.2.2 计数器3.2.3 自动重装载寄存器 3.3 触发控制器 四、PWM4.1 什么是PWM4.2 什么是占空比4.3 STM32F1 PWM介绍4.…

【Python 随练】打印菱形图案

题目&#xff1a; 打印出如下图案&#xff08;菱形&#xff09; ********* ****************简介&#xff1a; 在本篇博客中&#xff0c;我们将使用Python代码打印一个菱形图案。我们将提供问题的解析&#xff0c;并给出一个完整的代码示例来生成这个菱形图案。 问题分析&am…

阿里云开源离线同步工具DataX3.0,用于数据仓库、数据集市、数据备份

DataX是阿里云开源的一款离线数据同步工具&#xff0c;支持多种数据源和目的地的数据同步&#xff0c;包括但不限于MySQL、Oracle、HDFS、Hive、ODPS等。它可以通过配置文件来定义数据源和目的地的连接信息、数据同步方式、数据过滤等&#xff0c;从而实现数据的高效、稳定、可…

C++初阶—完善适配器(反向迭代器)

目录 0. 前言 1、反向迭代器定义 2、反向迭代器需要实现的相关函数 3、反向迭代器分析 4、针对vector物理空间结构分析 5、针对list物理空间结构分析 6、反向迭代器适配器的实现及测试 0. 前言 本篇文章主要根据前面所实现的STL中支持迭代器的容器进行完善&#xff0c;使…

Mysql数据库日志和数据的备份恢复(去看一看海吧)

文章目录 一、数据库备份的重要性二、数据库备份的分类1.物理备份&#xff1a;对数据库操作系统的物理文件&#xff08;如数据文件、日志文件等)的备份。2.逻辑备份&#xff1a;对数据库逻辑组件&#xff08;如表等数据库对象&#xff09;的备份&#xff0c;导出sql文件。3.完全…

被卖到 2w 的 ChatGPT 提示词 Prompt 你确定不想要吗?

有朋友说&#xff0c;用 ChatGPT 生成的文案刻板化&#xff0c;格式化&#xff0c;而且往往也不是我想要的。‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 想要用好 ChatGPT 人工智能工具太难了&#xff0c;想一个好的提示词&#xff0c;也太不容易了&#xff0c;ChatGPT 就像一个宝藏…

3ds MAX绘制休闲椅

首先绘制一个长方形&#xff0c;并用上边两端点和底边中点绘制一个样条曲线 将上述曲线旋转复制&#xff0c;形成一个交叉的样条曲线&#xff0c;并移动两端点的位置&#xff0c;形成一上一下的结构 这就是休闲椅的大致形状 通过创建线将四个端点连接起来&#xff0c;形成空间…