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

客户端登录检查:
spring-boot整合redis操作_redis

spring-boot整合redis操作_redis_02

模拟一段真实逻辑:

只做测试试用,实际使用需要加入适当空处理(防止缓存穿透,一直查库)过期时间时间随机(防止缓存同时失效,批量业务查库雪崩) 加锁(防止缓存击穿 某一时刻高并发查库)

    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;
}

结果展示:
spring-boot整合redis操作_redis_03

首次调用数据库 存储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有基于这两者的连接工厂类,所以这两者可以替换。