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

文章目录

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


分布式缓存

缓存使用场景

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

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

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

redis作缓存中间件

引入redis依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

配置redis

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

堆外内存溢出

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


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettucegroupId>
            <artifactId>lettuce-coreartifactId>
        exclusion>
    exclusions>
dependency>
<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
dependency>

缓存失效问题

缓存穿透

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

缓存雪崩

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

缓存击穿

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

Redisson分布式锁

导入依赖


<dependency>
    <groupId>org.redissongroupId>
    <artifactId>redissonartifactId>
    <version>3.12.0version>
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;
    }

缓存一致性解决

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

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

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

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

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

缓存-SpringCache

简介

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

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

image-20220208182936391

@Cacheable

引入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-cacheartifactId>
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注解

原理与不足

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

image-20220208191733676

你可能感兴趣的:(系统开发,缓存,笔记,谷粒商城,尚硅谷,分布式缓存,Java)