• Springboot自身缓存集合Redis的实现方式


    Springboot自身缓存集合Redis的实现方式

    一、Redis介绍
    Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统
    (区别于MySQL的二维表格的形式存储。)。和Memcache类似,但很大程度补偿了Memcache的不
    足。和Memcache一样,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到
    内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。所以Memcache的
    应用场景适用于缓存无需持久化的数据。而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修
    改操作写入追加的记录文件,实现数据的持久化。
    Redis的特点:
    1,Redis读取的速度是110000次/s,写的速度是81000次/s;
    2,原子 。Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
    3,支持多种数据结构:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)
    4,持久化,集群部署
    5,支持过期时间,支持事务,消息订阅

    Springboot缓存

    SpringBoot支持很多种缓存方式:redis、guava、ehcahe、jcache等等。
    缓存注解详解

    @CacheConfig

    主要用于配置该类中会用到的一些共用的缓存配置。 在这里@CacheConfig(cacheNames = “users”):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。

    @Cacheable

    配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。
    注解参数:

    value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存组件的名字,数组形式。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了

    key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:编写SpEL; #id,参数id的值 ,,#a0(第一个参数), #p0(和a0的一样的意义) ,#root.args[0]

    condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。

    unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。

    keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:key/keyGenerator:二选一使用;不能同时使用

    cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用

    cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。

    sync:是否使用异步模式

    @CachePut

    配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。 它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析
    @CacheEvict

    配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。也就是清除缓存除了同@Cacheable一样的参数之外,它还有下面两个参数:

    allEntries:非必需,默认为false。当为true时,会移除所有数据

    beforeInvocation:非必需,**默认为false,会在调用方法之后移除数据。**当为true时,会在调用方法之前移除数据。

    @Caching注解

    用于定义复杂的缓存规则,可以集成@Cacheable和 @CachePut

    // @Caching 定义复杂的缓存规则
        @Caching(
                cacheable = {
                        @Cacheable(/*value={"emp"},*/key = "#lastName")
                },
                put = {
                        @CachePut(/*value={"emp"},*/key = "#result.id"),
                        @CachePut(/*value={"emp"},*/key = "#result.email")
                }
        )
        public Employee getEmpByLastName(String lastName){
            return employeeMapper.getEmpByLastName(lastName);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    SpEL表达式用法

    在这里插入图片描述

    Spring Boot 整合 缓存开发

    相关注解

    (1)缓存@Cacheable
    根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不
    存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
    查看源码,属性值如下
    在这里插入图片描述
    (2)缓存@CachePut
    使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存
    中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
    查看源码,属性值如下:
    在这里插入图片描述
    (3)缓存@CacheEvict
    使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
    查看源码,属性值如下:
    在这里插入图片描述

    相关配置

     
                org.springframework.boot
                spring-boot-starter-data-redis
            
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    #数据库连接
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mytest_springboot_cache?useUnicode=true
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: lzh
     
      ## Redis 配置
      redis:
        ## Redis数据库索引(默认为0)
        database: 0
        ## Redis服务器地址
        host: 192.168.126.129
        ## Redis服务器连接端口
        port: 6379
        ## Redis服务器连接密码(默认为空)
        password:
        jedis:
          pool:
            ## 连接池最大连接数(使用负值表示没有限制)
            #spring.redis.pool.max-active=8
            max-active: 8
            ## 连接池最大阻塞等待时间(使用负值表示没有限制)
            #spring.redis.pool.max-wait=-1
            max-wait: -1
            ## 连接池中的最大空闲连接
            #spring.redis.pool.max-idle=8
            max-idle: 8
            ## 连接池中的最小空闲连接
            #spring.redis.pool.min-idle=0
            min-idle: 0
        ## 连接超时时间(毫秒)
        timeout: 1200
    
    
    • 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

    Redis集群配置

    spring:
      redis:
        cluster:
          nodes:
            - 192.168.1.236:7001
            - 192.168.1.236:7002
            - 192.168.1.236:7003
            - 192.168.1.244:7004
            - 192.168.1.244:7005
            - 192.168.1.244:7006
          max-redirects: 3  # 获取失败 最大重定向次数
        pool:
          max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
          max-idle: 10    # 连接池中的最大空闲连接
          max-wait: -1   # 连接池最大阻塞等待时间(使用负值表示没有限制)
          min-idle:  5     # 连接池中的最小空闲连接
        timeout: 6000  # 连接超时时长(毫秒)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    编写java配置类

    @Configuration
    @EnableCaching
    public class RedisConfig extends CachingConfigurerSupport {
     
        /**
         * 选择redis作为默认缓存工具
         * @param redisConnectionFactory
         * @return
         */
        /*@Bean
        //springboot 1.xx
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
            return rcm;
        }*/
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
            return RedisCacheManager
                    .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                    .cacheDefaults(redisCacheConfiguration).build();
        }
     
        /**
         * retemplate相关配置
         * @param factory
         * @return
         */
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
     
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            // 配置连接工厂
            template.setConnectionFactory(factory);
     
            //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
            Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
     
            ObjectMapper om = new ObjectMapper();
            // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jacksonSeial.setObjectMapper(om);
     
            // 值采用json序列化
            template.setValueSerializer(jacksonSeial);
            //使用StringRedisSerializer来序列化和反序列化redis的key值
            template.setKeySerializer(new StringRedisSerializer());
     
            // 设置hash key 和value序列化模式
            template.setHashKeySerializer(new StringRedisSerializer());
            template.setHashValueSerializer(jacksonSeial);
            template.afterPropertiesSet();
     
            return template;
        }
     
        /**
         * 对hash类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForHash();
        }
     
        /**
         * 对redis字符串类型数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForValue();
        }
     
        /**
         * 对链表类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForList();
        }
     
        /**
         * 对无序集合类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForSet();
        }
     
        /**
         * 对有序集合类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForZSet();
        }
    }
    
    
    • 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

    这里主要是使用RedisTemplate来对远程redis操作,每次访问controller暴露的接口,首先判断redis缓存中是否存在该数据,若不存在就从数据库中读取数据,然后保存到redis缓存中,当下次访问的时候,就直接从缓存中取出来。这样就不用每次都执行sql语句,能够提高访问速度。 但是在保存数据到缓存中,通过设置键和值和超时删除,注意设置超时删除缓存时间不要太长,否则会给服务器带来压力。

    也可以使用注解的形式,使用方式和Ehcache的使用方式一致。

    其他Redis配置

    /**
     * @Description: redis配置文件
     * @Author: xudada
     * @Date: 2022-8-03 20:46
     */
    @EnableCaching //开启缓存
    @Configuration  //配置类
    public class RedisConfig extends CachingConfigurerSupport {
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setConnectionFactory(factory);
            //key序列化方式
            template.setKeySerializer(redisSerializer);
            //value序列化
            template.setValueSerializer(jackson2JsonRedisSerializer);
            //value hashmap序列化
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            return template;
        }
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            //解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // 配置序列化(解决乱码的问题),过期时间600秒
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofSeconds(600))
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    .build();
            return cacheManager;
        }
    }
    
    
    • 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

    在service上加返回的数据会保存倒redis通过注解@Cacheable进行配置

    @Service
    public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner>
    implements CrmBannerService {
    @Cacheable(value = "banner", key = "'selectIndexList'")
    @Override
    public List<CrmBanner> selectIndexList() {
    List<CrmBanner> list = baseMapper.selectList(new
    QueryWrapper<CrmBanner>().orderByDesc("sort"));
    return list;
     }
    @Override
    public void pageBanner(Page<CrmBanner> pageParam, Object o) {
    baseMapper.selectPage(pageParam,null);
     }
    @Override
    public CrmBanner getBannerById(String id) {
    return baseMapper.selectById(id);
     }
    @CacheEvict(value = "banner", allEntries=true)
    @Override
    public void saveBanner(CrmBanner banner) {
    baseMapper.insert(banner);
     }
    
    @CacheEvict(value = "banner", allEntries=true)
    @Override
    public void updateBannerById(CrmBanner banner) {
    baseMapper.updateById(banner);
     }
    @CacheEvict(value = "banner", allEntries=true)
    @Override
    public void removeBannerById(String id) {
    baseMapper.deleteById(id);
     }
    }
    
    • 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

    案例

    @Service
    public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {
    
        /**
         * 查询所有轮播图,并存入缓存中做为热数据
         * @return
         */
        @Cacheable(value = "banner",key = "'selectIndexList'")//存放在缓存中
        @Override
        public List<CrmBanner> selectBanner() {
            //根据id进行降序排序,显示排列后前两条记录
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.orderByAsc("sort");
            List<CrmBanner> list = baseMapper.selectList(wrapper);
            return list;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    结果
    在这里插入图片描述

  • 相关阅读:
    【监督学习】基于合取子句进化算法(CCEA)和析取范式进化算法(DNFEA)解决分类问题(Matlab代码实现)
    了解多人游戏下的客户端与服务器体系结构
    UGeek大咖说 | 顺丰科技:全链路压测中的可观测性实践
    AI应用新时代的起点,亚马逊云科技加速大模型应用
    变分自编码器VAE
    ppt太大怎么压缩变小?ppt压缩方法和步骤
    Docker清理
    numpy生成数组
    Sqoop与Shell脚本数据迁移实战
    实验三 进程管理
  • 原文地址:https://blog.csdn.net/m0_46607044/article/details/126149981