springboot整合redis缓存一些知识点

前言

最近在做智能家居平台,考虑到家居的控制需要快速的响应于是打算使用redis缓存。一方面减少数据库压力另一方面又能提高响应速度。项目中使用的技术栈基本上都是大家熟悉的springboot全家桶,在springboot2.x以后操作redis的客户端推荐使用lettuce(生菜)取代jedis。

jedis的劣势主要在于直连redis,又无法做到弹性收缩。

一、配置文件

application.yml文件中的内容

1spring: 2 application: 3 name: simple-lettuce 4 cache: 5 type: redis 6 redis: 7 # 缓存超时时间ms 8 time-to-live: 60000 9 # 是否缓存空值 10 cache-null-values: true 11 redis: 12 host: 127.0.0.1 13 port: 6379 14 password: 123456 15 # 连接超时时间(毫秒) 16 timeout: 60000 17 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0 18 database: 1 19 # spring2.x redis client 采用了lettuce(生菜),放弃使用jedis 20 lettuce: 21 # 关闭超时时间 22 shutdown-timeout: 30000 23 pool: 24 # 连接池最大连接数(使用负值表示没有限制) 默认 8 25 max-active: 30 26 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 27 max-wait: -1 28 # 连接池中的最大空闲连接 默认 8 29 max-idle: 8 30 # 连接池中的最小空闲连接 默认 0 31 min-idle: 0

说明:

  • spring.cache.type: redis

已经表明使用项目采用redis做为缓存方式。

  • spring.cache.redis.cache-null-values: true

表示是否缓存空值,一般情况下是允许的。因为这涉及到缓存的三大问题:缓存穿透、缓存雪崩、缓存击穿。

如果设置false即不允许缓存空值,这样会导致很多请求数据库没有的数据时,不会缓存到redis导致每次都会请求到数据库。这种情况即:缓存穿透。

具体想初步了解这些概念可以参考文章:缓存三大问题及解决方案!

二、config配置类

1@Configuration 2@EnableCaching 3public class RedisTemplateConfig extends CachingConfigurerSupport { 4 5 private static Map<String, RedisCacheConfiguration> cacheMap = Maps.newHashMap(); 6 7 @Bean(name = "stringRedisTemplate") 8 @ConditionalOnMissingBean(name = "stringRedisTemplate") //表示:如果容器已经有redisTemplate bean就不再注入 9 public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory); 10 } 11 12 @Bean(name = "redisTemplate") 13 @ConditionalOnMissingBean(name = "redisTemplate") 14 public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { 15 System.out.println("RedisTemplateConfig.RedisTemplate"); 16 RedisTemplate<String, Object> template = new RedisTemplate<>(); 17 // key的序列化采用StringRedisSerializer 18 template.setKeySerializer(keySerializer()); 19 template.setHashKeySerializer(keySerializer()); 20 // value值的序列化采用fastJsonRedisSerializer 21 template.setValueSerializer(valueSerializer()); //使用fastjson序列化 22 template.setHashValueSerializer(valueSerializer()); //使用fastjson序列化 23 template.setConnectionFactory(lettuceConnectionFactory); 24 return template; 25 } 26 27 /** 28 * 添加自定义缓存异常处理 29 * 当缓存读写异常时,忽略异常 30 * 参考:https://blog.csdn.net/sz85850597/article/details/89301331 31 */ 32 @Override 33 public CacheErrorHandler errorHandler() { 34 return new IgnoreCacheErrorHandler(); 35 } 36 37 @SuppressWarnings("Duplicates") 38 @Bean 39 @Primary//当有多个管理器的时候,必须使用该注解在一个管理器上注释:表示该管理器为默认的管理器 40 public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { 41 // 默认配置 42 RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() 43 .serializeKeysWith(keyPair()) 44 .serializeValuesWith(valuePair()) 45 .entryTtl(Duration.ofSeconds(DEFAULT_TTL_SECS)) //设置过期时间 46 .disableCachingNullValues(); 47 48 // 其它配置 49 for(MyCaches cache : MyCaches.values()) { 50 cacheMap.put(cache.name(), 51 RedisCacheConfiguration.defaultCacheConfig() 52 .serializeKeysWith(keyPair()) 53 .serializeValuesWith(valuePair()) 54 .entryTtl(cache.getTtl()) 55 // .disableCachingNullValues() // 表示不允许缓存空值 56 .disableKeyPrefix() // 不使用默认前缀 57 // .prefixKeysWith("mytest") // 添加自定义前缀 58 ); 59 } 60 61 /** 遍历MyCaches添加缓存配置*/ 62 RedisCacheManager cacheManager = RedisCacheManager.builder( 63 RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory) 64 ) 65 .cacheDefaults(defaultCacheConfig) 66 .withInitialCacheConfigurations(cacheMap) 67 .transactionAware() 68 .build(); 69 70 ParserConfig.getGlobalInstance().addAccept("mypackage.db.entity."); 71 return cacheManager; 72 } 73 74 /** 75 * key序列化方式 76 * @return 77 */ 78 private RedisSerializationContext.SerializationPair<String> keyPair() { 79 RedisSerializationContext.SerializationPair<String> keyPair = 80 RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()); 81 return keyPair; 82 } 83 84 private RedisSerializer<String> keySerializer() { 85 return new StringRedisSerializer(); 86 } 87 88 /** 89 * value序列化方式 90 * @return 91 */ 92 private RedisSerializationContext.SerializationPair<Object> valuePair() { 93 RedisSerializationContext.SerializationPair<Object> valuePair = 94 RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()); 95 return valuePair; 96 } 97 98 /** 99 * 使用fastjson序列化 100 * @return 101 */ 102 private RedisSerializer<Object> valueSerializer() { 103 MyFastJsonRedisSerializer<Object> fastJsonRedisSerializer = new MyFastJsonRedisSerializer<>(Object.class); 104 return fastJsonRedisSerializer; 105 } 106 107 @Getter 108 private enum MyCaches { 109 defaultCache(Duration.ofDays(1)), 110 MyCaches(Duration.ofMinutes(10)); 111 112 MyCaches(Duration ttl) { 113 this.ttl = ttl; 114 } 115 /** 失效时间 */ 116 private Duration ttl = Duration.ofHours(1); 117 } 118}

说明

1. 类上的注解@EnableCaching

表明开启缓存功能。

2. extends CachingConfigurerSupport

这个类就很丰富了,其实如果没有什么特别操作也可以不用继承这个类。

这个类可以支持动态选择缓存方式,比如项目中不止一种缓存方案,有可能有ehcache那么可以自定义在什么情况下使用redis使用情况下使用ehcache。还有一些有关异常的处理。我也不是很懂具体可以参考:

springboot(25)自定义缓存读写机制CachingConfigurerSupport

3. StringRedisTemplate和RedisTemplate的使用

(1)两者的主要差别是:如果你只想缓存简单的字符串选择StringRedisTemplate是一个明智的举措。如果想使用redis缓存一些对象数据肯定是要选择RedisTemplate。

(2)RedisTemplate需要注意一点就是要怎么选择序列化工具。默认使用jdk的序列化缓存数据后即value值是无法直接阅读的而存的二进制数据。

通常我们会选择jackson或者fastjson来序列化对象,把对象转换成json格式。两者序列化对象后都会在头部加上一个对象类路径如:@type com.mypackage.entity.User。这个也算是一种安全策略。

比如使用fastjosn就会在cacheManager中指定序列化对象的包所在位置白名单:ParserConfig.getGlobalInstance().addAccept("mypackage.db.entity.");

fastjson官方说明:https://github.com/alibaba/fastjson/wiki/enable\_autotype

(3)还有需要注意如果value是string类型。RedisTemplate会在字符串外围再加一对双引号,如""abc""。如果使用StringRedisTemplate读取则能得到abc,但是我在项目使用Jedis读取就成了"abc"这就导致这些字符串无法被反序列化。

(4)StringRedisTemplate和RedisTemplate两者数据是相互隔离的,如果使用StringRedisTemplate存入的数据使用RedisTemplate是无法读取、删除的。

三、缓存注解使用

@Cacheable 使用在查询方法上

@CachePut 使用在更新、保存方法上

@CacheEvict 使用在删除方法上

需要注意的是@Cacheable、@CachePut方法一定要有返回被缓存对象。因为注解使用的AOP切面如果没有返回值表示缓存对象为空值。

@CacheConfig注解在类上,可以选择使用哪个缓存、缓存管理器、Key生成器

好了以上就是最近在项目中的一些知识点总结,如果以后使用缓存有新的体会我会同步更新的。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )