当前位置:   article > 正文

springboot整合springcache(数据库redis)_springboot集成cache数据库

springboot集成cache数据库

系列文章目录


一、说明:

spring cache官方文档

spEl语法说明==>官方文档

  1. Spring Cache是一项缓存
  2. 可以注解就达到缓存效果
  3. 小编的案例是基于springboot,整合是redis

官网springcache介绍目录:
在这里插入图片描述

官网的注解一共有5个,如图:
在这里插入图片描述
小编给他翻译下

注解说明
@Cacheable触发将数据保存到缓存的操作(启动缓存)
@CacheEvict触发将数据从缓存删除的操纵(失效模式)
@CachePut不影响方法执行更新缓存(双写模式)
@Caching:组合以上多个操作(点击注解看源码就知道了,组合注解)
@CacheConfig在类级别共享缓存的相同配置

概念图:
在这里插入图片描述


二、原理梳理

2.1、 比较重要的源码类

  1. CacheAutoConfiguration 缓存的自动配置
  2. 小编用的类型是redis所以看 RedisCacheConfiguration
  3. CacheManager 缓存管理者
  4. 小编是类型是redis所以看 RedisCacheManager
  5. CacheProperties 缓存默认配置
  6. idea搜索的方法 双击shift 或者 ctrl n

2.2、 原理说明:

流程说明:

 CacheAutoConfiguration  =>  RedisCacheConfiguration =>
 自动配置了RedisCacheManager =>  初始化所有的缓存 => 
 每个缓存决定使用什么配置=>
 =>如果RredisCacheConfiguration有就用已有的,没有就用默认配置(CacheProperties=>想改缓存的配置,只要给容器中放一个RredisCacheConfiguration即可
 =>就会应用到当前RedisCacheManager管理的所有缓存分区中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三、默认缓存的数据类型

在默认配置下,springcache给我们缓存的试用jdk序列化过的数据

我们通常是缓存Json字符串,因为使用Json能跨语言,跨平台进行交互

所以我们要修改他的默认配置,包括ttl(过期时间)、存储格式、等…


四、整合

项目结构:

在这里插入图片描述

4.1、引入依赖

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

<!--spring cache-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.2、先看下配置源码是怎么样的

RedisCacheConfiguration
在这里插入图片描述

4.3、创建配置类(照猫画虎)

注意事项:要让原本配置文件的一些配置生效

开启属性绑定配置的功能
@EnableConfigurationProperties(CacheProperties.class)

MyCacheConfig

package sqy.config.cache;

import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;


/**
 * @author suqinyi
 * @Date 2022/1/13 -更新
 */
@EnableConfigurationProperties(CacheProperties.class)//开启属性绑定配置的功能
@Configuration //配置类
@EnableCaching //开启缓存功能
public class MyCacheConfig {

//    第一种、从容器里面拿
//    @Autowired
//    CacheProperties cacheProperties;

    /**
     * 配置文件中的很多东西没用上
     *      1、原来和配置文件绑定的配置类是这个样子的
     *          @ConfigurationProperties(
     *              prefix = "spring.cache"
     *          )
     *          public class CacheProperties
     *
     *      2、要让他生效
     *          @EnableConfigurationProperties(CacheProperties.class)//开启属性绑定配置的功能
     *
     */

    //第二种、因为注入了容器,参数属性spring会自己去容器里面找 (CacheProperties cacheProperties)
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//        config=config.entryTtl();
        config= config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        /**
         * GenericFastJsonRedisSerializer   fastjson家族的
         * GenericJackson2JsonRedisSerializer   spring自带的 package org.springframework.data.redis.serializer;
         */
        //指定序列化-Jackson
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //指定序列化-fastjson
        //config= config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));


        //从所以配置中取出redis的配置
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        //将配置文件中所有的配置都生效(之间从源码里面拷 RedisCacheConfiguration)
        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;

    }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

4.4、配置文件(application.properties

#类型指定redis
spring.cache.type=redis

#一个小时,以毫秒为单位
spring.cache.redis.time-to-live=3600000
#给缓存的建都起一个前缀。  如果指定了前缀就用我们指定的,如果没有就默认使用缓存的名字作为前缀,一般不指定
#spring.cache.redis.key-prefix=CACHE_
#指定是否使用前缀
spring.cache.redis.use-key-prefix=true
#是否缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.5、使用

在方法上标注注解就可以

4.5.1、@Cacheable(开启缓存功能)

将查询到的结果存入缓存

注意事项

  1. 有对应的缓存就不进入方法 [需要返回值,没有返回值缓存空值]
  2. @Cacheable并没有单独的失效时间的方法。
  3. 但是可以在CacheManager配置,在+上自动刷新的功能,但是这样的的操作比较繁琐。如果不设置,只有统一的过期时间很容易导致缓存雪崩的问题

01、有返回缓存

	/**
     * TODO @Cacheable并没有单独的失效时间的方法。
     *      但是可以在CacheManager配置,在+上自动刷新的功能,但是这样的的操作比较繁琐。
     *      如果不设置,只有统一的过期时间很容易导致缓存雪崩的问题。
     *
     * @Cacheable开启缓存功能 有对应的缓存就不进入方法 [需要返回值,没有返回值缓存空值]
     * value = "student", 【key ="#root.methodName" 或 key = "'名称'" 或 key = "#传入的参数" 或  key = "#接收参数的实体.属性"
     * 更多方式看spEl语法 】
     * <p>
     * student是分区名字
     * #root.methodName是spEl语法 也就是方法名 testCache
     * <p>
     * 在redis里面 他的存储就是 student::testCache
     * 同一个业务类型是数据放在同一个分区,树形结构,
     * 类如:a包里面有b,c。  b和c就是具体缓存。a就是名称空间
     * @Cacheable(value = {"student"},key ="#root.method.name" ,sync = true)
     * sync = true	这个属性的意思是加锁,解决缓存击穿问题
     */
    //localhost:8080/testCache
    @Cacheable(value = "student", key = "#root.method.name")
    @GetMapping("/saveCache01")
    public HashMap<String, List<Student>> saveCache01() {
        System.out.println("方法saveCache01执行");
        HashMap<String, List<Student>> map = new HashMap<String, List<Student>>();
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("ssm", "888888"));
        studentList.add(new Student("boot", "123456"));
        studentList.add(new Student("cloud", "741147"));
        map.put("studentList", studentList);
        System.out.println("缓存成功");
        return map;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

02、无返回值,或者返回空,缓存空值

/**
     * 1、返回值为void 缓存空值
     * 2、返回null 缓存空值
     * TODO 【NullValue】
     *  sr+org.springframework.cache . support.NullValue xp
     */
    @Cacheable(value = "student", key = "#root.method.name")
    @GetMapping("/saveCache02")
    public void saveCache02() {
        System.out.println("方法saveCache02执行");
        HashMap<String, List<Student>> map = new HashMap<String, List<Student>>();
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("ssm", "888888"));
        studentList.add(new Student("boot", "123456"));
        studentList.add(new Student("cloud", "741147"));
        map.put("studentList", studentList);
        System.out.println("缓存成功");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.5.2、@CacheEvict(失效模式)

简单的说:就是你执行了修改/删除的操作,他会将缓存里面数据给清除

第一种、删除单个

	 /**
     * 失效模式(可以叫删除模式)
     * value = "student",key = "'saveCache01'" 注意单引号
     * student是分区名字
     * saveCache01是缓存的key值。使用@Cacheable缓存的时候spEl我们指定的方法名
     * todo @CacheEvict(value = "student",allEntries = true)  allEntries = true表示删除student分区下所有数据
     */
    @CacheEvict(value = "student", key = "'saveCache01'")//缓存 失效模式
    @GetMapping("/updateData")
    public void updateData() {
        System.out.println("执行失效模式,删除缓存");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第二种、删除多个,将整个分区的缓存都清除

好比说 a下面有b和c 。将b和c一起删除

所以:同一业务\同一类型缓存的数据要放在同一的分区下面

	//1、失效模式 
	//2、allEntries = true 删除分区所有的数据
 	@CacheEvict(value = "category",allEntries = true)
    @Transactional
    @Override
    public void updateCascade(CategoryEntity category) {
		//service的业务代码
		
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.5.3、@Caching(组合使用)

源码:
在这里插入图片描述
比如说要让哪个分区下面的哪个缓存失效(删除)

	/**
     * TODO @Caching 组合注解 允许在同一方法上使用多个嵌套的 @Cacheable、@CachePut和@CacheEvict
     * value ==> student分区
     * key   ==> saveCache01 缓存的key名称
     * 个人感觉还是使用@CacheEvict的删除分区里面全部的缓存方便点
     */
    @Caching(evict = {
            @CacheEvict(value = "student", key = "'saveCache01'"),
            @CacheEvict(value = "student", key = "'saveCache02'")
    })
    @GetMapping("/selectEvict")
    public void selectEvict() {
        System.out.println("组合注解=>指定分区下失效的key");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.5.4、@CachePut(双写模式)

/**
     *  @CachePut
     * 根据返回值更新缓存,【双写模式】,用得比较少,这边就不搞了
     */
    @CachePut
    @GetMapping("/testPut")
    public void testPut() {
        System.out.println("双写模式");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.5.5、redisTemplate获取数据方法

  1. 这边使用的是StringRedisTemplate
  2. 没使用RedisTemplate因为没去重构序列化器

	@Autowired
    StringRedisTemplate redisTemplate;//采用string的序列化构造器

	
	 @GetMapping("/getCache")
    public void getCache() {
        HashMap<String, List<Student>> map = new HashMap<String, List<Student>>();
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("ssm", "888888"));
        studentList.add(new Student("boot", "123456"));
        studentList.add(new Student("cloud", "741147"));
        map.put("studentList", studentList);
        redisTemplate.opsForValue().set("student::saveCache03", String.valueOf(map));
        System.out.println("获取缓存");
        String s = redisTemplate.opsForValue().get("student::saveCache03");
        System.out.println(s);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.5.6、测试效果

01、
在这里插入图片描述
02、
在这里插入图片描述
03、
在这里插入图片描述
04、
在这里插入图片描述

五、简单实战案例

实体类:

package sqy.pojo;

import org.springframework.stereotype.Component;

/**
 * @author suqinyi
 * @Date 2022/1/21
 * user表
 */
//@Component
public class User {

    private String id;//主键
    private String userName;//用户名
    private String password;//密码

	//get /set /构造、tostring...省略
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

实战测试:

package sqy.controller;

import org.springframework.cache.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import sqy.pojo.User;

import java.io.IOException;

/**
 * @author suqinyi
 * @Date 2022/1/21
 * springCache的实战使用【sqy测】--- TODO 缓存数据不要去数据库直接改数据!!!
 */
@RestController
@CacheConfig(cacheNames = "user")//指定缓存空间的名称
//@Lazy//懒加载--这个有毒测试着玩,用于注入的service
public class UserCacheController {

    
    /**
     * todo 说明
     *
     * @Cacheable 必须要有返回【实体、list、map】-- 用于 查询
     * @CachePut 必须要有返回【实体、list、map】-- 用于 新增、修改
     * @CacheEvict 返回值为void--用于 删除
     * @CacheConfig 配置      --通常用于指定缓存空间名称较多
     * @Cacheable 组合注解  [ cacheable() 、put()、evict() ] 存、加、删
     */
//    private static List<User> list = new ArrayList<>();


    //模拟从数据库获取到数据
    private User getUserData01() {
        User user = new User("001", "userOO1", "123456");
        return user;
    }
    //模拟从数据库获取到数据
    private User getUserData02() {
        User user = new User("002", "userOO2", "789456");
        return user;
    }




    /**
     * 主键查询--这个缓存是在service做,测试案例我就之间在controller写了
     * 名称空间value 在controller统一指定了
     * 缓存key为 名称空间::id
     *
     * @Cacheable(key = "#qvo.id",unless = "#result!=null" )
     * unless = "#result!=null" 返回的结果不为空才缓存
     * 这个方法不缓存空值
     * localhost:8080/findById
     * post  json  {"id":"1"}
     */
    @PostMapping("/findById")
    @Cacheable(key = "#qvo.id")
    public User findById(@RequestBody User qvo) {
        System.out.println("执行方法-findById");
        //查到数据
        if ("001".equals(qvo.getId())) {
            User user = getUserData01();
            return user;
        } else {
            return null;
        }
    }


    /**
     * 用户名查询
     * 名称空间value 在controller统一指定了
     * 缓存key为 名称空间::id
     * 这个查询缓存空值  sr+org.springframework.cache . support.NullValue xp
     * localhost:8080/findByName
     * post json {"userName":"userOO1"}
     */
    @PostMapping("/findByName")
    @Cacheable(key = "#qvo.userName")
    public User findByName(@RequestBody User qvo) {
        System.out.println("执行方法-findByName");
        //查到数据
        if ("userOO1".equals(qvo.getUserName())) {
            User user = getUserData01();
            return user;
        } else {
            return null;
        }
    }


    /**
     * 新增数据-测试 @Caching组合注解
     * 缓存新增的id和用户名
     * condition = "#result != null" 当结果不为空时缓存
     * localhost:8080/userSave
     * post json  {"id":"002","userName":"user002"}
     */
    @PostMapping("/userSave")
    @Caching(put = {
            @CachePut(key = "#result.id", condition = "#result != null"),
            @CachePut(key = "#result.userName", condition = "#result != null")
    })
    public User userSave(@RequestBody User vo) throws IOException {
        if ("002".equals(vo.getId()) && "user002".equals(vo.getUserName())) {
            //1、存入数据库 2、查询数据返回
            System.out.println(vo);
            return vo;
        } else {
            return null;
        }
    }

    /**
     * 修改数据-测试 @Caching组合注解---
     * 【有双写模式@CachePut 和 失效模式@CacheEvict 】
     * 缓存新增的id和用户名
     * condition = "#result != null" 当结果不为空时缓存
     *
     *  localhost:8080/userUpdate
     *  post json  {"id":"002","userName":"user003"}
     */
    @PostMapping("/userUpdate")
    @Caching(put = {
            @CachePut(key = "#result.id", condition = "#result != null"),
            @CachePut(key = "#result.userName", condition = "#result != null")
    })
    public User userUpdate(@RequestBody User vo) {
        //将原本2号数据user002改成user003
        if ("002".equals(vo.getId()) && "user003".equals(vo.getUserName())) {
            //查数据
            User user = getUserData02();
            //更新
            user.setUserName(vo.getUserName());
            user.setPassword(vo.getPassword());
            return user;
        } else {
            return null;
        }
    }


    /**
     * 删除数据
     * 缓存新增的id和用户名
     * condition = "#result != null" 当结果不为空时缓存
     * localhost:8080/userDel
     * post json {"id":"001","userName":"user001"}
     *
     */
    @PostMapping("/userDel")
    @Caching(evict = {
            @CacheEvict(key = "#vo.id"),
            @CacheEvict(key = "#vo.userName")
    })
    public void userDel(@RequestBody User vo) throws Exception {
        //删除1号数据
        if ("001".equals(vo.getId()) && "user001".equals(vo.getUserName())) {
            //1、查数据
            User user = getUserData01();
            System.out.println(user);
            //2、删除  ...
        } else {
            throw new Exception("id不是1,不能删");
        }
    }

    /**
     * 查询-有缓存就查缓存,不走方法
     * localhost:8080/findUser
     * post josn {"id":"001","userName":"user001"}
     */
    @PostMapping("/findUser")
    public void findUser(@RequestBody User vo) throws Exception {
        if ("001".equals(vo.getId())) {
            User user = this.findById(vo);
            System.out.println("findById==>" + user);
        }
        if ("user001".equals(vo.getUserName())) {
            User user = this.findByName(vo);
            System.out.println("findByName==>" + user);
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189

效果:

在这里插入图片描述

六、小归纳

@Cacheable
     代表当前方法的结果需要缓存
      如果缓存种有,方法不用调用
      如果缓存中没有,就会调用方法,将方法的结果放入缓存
    
      @Cacheable(value = {"category"})
      		每一个需要缓存的数据都来指定要放到那个名字缓存。[缓存的分区(推荐按照业务类型划分)]
    
      默认行为
          1、缓存中有,方法不用调用
          2、key默认生成,缓存的名字::SimpleKey [](自主生成的key值)
          3、缓存value的值,默认采用jdk序列化机制,将序列化后的数据存到redis
          4、默认的ttl时间是 -1 代表永不过期
    
      自定义操作:
          1)、指定生成的缓存使用的key:      使用key属性指定,接收一个spEl表达式
                    spEl语法说明==>官方表达式地址说明:https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-spel-context
    
          2)、指定缓存的数据的存活时间:      在配置文件中修改ttl
          3)、将数据保存为json (因为json跨平台跨语言能交互)
          	  源码类
             	 CacheAutoConfiguration
              	 RedisCacheConfiguration
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

七 、Spring-Cache的不足:

SpringCache对读模式都进行处理,解决了缓存击穿,缓存穿透,缓存雪崩的问题
但是对写模式并没有去处理

读模式(SpringCache都处理了)

  1. 缓存穿透:查询一个null数据。 解决方法:缓存空数据。 spring.cache.redis.use-key-prefix=true
  2. 缓存击穿:大量并发进来,查询一个正好过期的数据。 解决方法:加锁 :默认是无加锁的; sync = true(加锁,解决缓存击穿)
  3. 缓存雪崩:大量的key同时过期 解决方法:加随机时间 (很容易弄巧成拙,要注意) spring.cache.redis.time-to-live=3600000

写模式(SpringCache没有管)

我们该如何解决(3种方式)

  1. 读写加锁
  2. 引入中间件Canal,感知到mysql的更新去更新
  3. 读多写多的,直接去数据库查询

八、个人推荐:

  1. 如果集成最后要配置缓存管理。要加锁、加自动过期等配置
  2. 当然还有其他选择如:
    redis、
    redisson 分布式、
    caffeine 这个会增大服务器压力、
    Guava 是Google Fuava中的一个内存缓存模块、
    Ehcache 是一个Java实现的开源分布式缓存框、
    JBoss Cache 分布式

九、小结:

常规数据:(读多写少,即使性、一致性要求不高的的数据)
完成可以使用spring-cache,写模式(是要缓存的数据有过期时间就足够了)


特殊数据: 特殊设计

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/164606
推荐阅读
相关标签
  

闽ICP备14008679号