spring-boot整合redis操作
1. 引入redis的starter
pom.xml
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
2. 配置redis
application.yml
spring:
redis:
host: 192.168.31.125
password:
port: 6379
3. 引入stringRedisTemplate
@Autowired
StringRedisTemplate stringRedisTemplate;
...
@Test
public void testRedis(){
//保存
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set("hello","world_" + UUID.randomUUID().toString());
//查询
String hello = ops.get("hello");
System.out.println(hello);
}
运行测试:
2021-11-22 15:40:40.198 INFO 13260 --- [ main] c.a.n.client.config.impl.ClientWorker : [fixed-127.0.0.1_8848-561fc56a-09b9-4827-afff-dc46de5469ed] [subscribe] mybatis.yml+dev+561fc56a-09b9-4827-afff-dc46de5469ed
2021-11-22 15:40:40.198 INFO 13260 --- [ main] c.a.nacos.client.config.impl.CacheData : [fixed-127.0.0.1_8848-561fc56a-09b9-4827-afff-dc46de5469ed] [add-listener] ok, tenant=561fc56a-09b9-4827-afff-dc46de5469ed, dataId=mybatis.yml, group=dev, cnt=1
2021-11-22 15:40:41.043 INFO 13260 --- [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library
2021-11-22 15:40:41.046 INFO 13260 --- [ main] io.lettuce.core.KqueueProvider : Starting without optional kqueue library
world_b96c7f9c-3dbc-45ae-85f1-0bd47a9f512c
客户端登录检查:
模拟一段真实逻辑:
只做测试试用,实际使用需要加入适当空处理(防止缓存穿透,一直查库)过期时间时间随机(防止缓存同时失效,批量业务查库雪崩) 加锁(防止缓存击穿 某一时刻高并发查库)
public Map<String, List<Catelog2Vo>> getCatalogJson() {
//给缓存存放json 拿出的json字符串需要逆转为对象类型【序列化与反序列化】
//1.从缓存取出的json
String redisString = stringRedisTemplate.opsForValue().get(RedisConstant.INDEX_CATALOGJSON);
if(StringUtils.isEmpty(redisString)){
System.out.println("经过查数据库...");
//2.缓存中没有 查询数据库
Map<String, List<Catelog2Vo>> catlogJsonFromDb = getCatalogJsonFromDb();
//3. 数据库查询结构 转成json保存缓存
String s = JSON.toJSONString(catlogJsonFromDb);
stringRedisTemplate.opsForValue().set(RedisConstant.INDEX_CATALOGJSON,s,1, TimeUnit.DAYS);
return catlogJsonFromDb;
}
//2. - 直接把缓存取出结果 逆转指定类型
Map<String, List<Catelog2Vo>> result = JSON.parseObject(redisString, new TypeReference< Map<String, List<Catelog2Vo>>>(){});
return result;
}
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDb() {
//数据库逻辑
return res;
}
结果展示:
首次调用数据库 存储redis消耗了6283ms
后续的直接redis获取数据消耗之多在百毫秒级别。
对性能有明显的提升
拓展
记录一个谷粒学院大神解决问题的过程:
springboot整合redis后,做压力测试或者长时间线上运行后产生:堆外内存溢出 OutOfDirectMemoryError
分析:
springboot2.0以后默认使用的lettuce作为操作redis的客户端。它使用netty进行网络通信。
leetuce的bug导致netty对外内存溢出。 大神翻阅代码后找出 netty的底层自己会统计内存容量使用量,netty会根据开销减去内存剩余量。推测netty客户端某处操作时,没有及时释放内存触发异常。
大神提出两个方案解决:
1升级lettuce客户端 2.更换使用jedis
大神操作是更换jedis(截至目前lettuce没有好的解决方案)
原先的pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
修改(剔除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>
<version>2.1.8.RELEASE</version>
</dependency>
大神的补充讲解:
redisTemplate:
lettuce 、jedis两者都是操作redis底层客户端,封装了redis的一些api.redisTemplate是对这两者的在此封装。
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
...
...
redisTemplate有基于这两者的连接工厂类,所以这两者可以替换。