ElIauk

Redis 安装与 Spring Boot 集成

先把 Redis 跑起来 本地学 Redis,最省事的方式是用 Docker。下面这条命令启动一个 7.x 实例,开启 AOF 持久化并设置密码: docker run -d --name redis7 \ -p 6379:6379 \ -v /data/redis/data:/data \

评论
Redis 安装与 Spring Boot 集成

先把 Redis 跑起来

本地学 Redis,最省事的方式是用 Docker。下面这条命令启动一个 7.x 实例,开启 AOF 持久化并设置密码:

docker run -d --name redis7 \
  -p 6379:6379 \
  -v /data/redis/data:/data \
  redis:7.2 \
  redis-server --appendonly yes --requirepass "change-me"

启动后先验证:

docker exec -it redis7 redis-cli -a change-me ping
# PONG

如果要在测试环境长期使用,建议挂载配置文件,把 bind、protected-mode、maxmemory、appendfsync 这些参数显式写清楚,而不是全部靠命令行。容器里的默认配置和宿主机安装的 Redis 不完全一样,尤其是绑定地址和持久化目录。

Spring Boot 集成

引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

第二个依赖容易被忘掉。Lettuce 连接池的配置只有在类路径上有 commons-pool2 时才生效,没有它,spring.redis.lettuce.pool.* 写了也不会报错,只是不生效。

配置文件的写法要注意版本差异:Spring Boot 2.x 用 spring.redis.*,Spring Boot 3.0 改成了 spring.data.redis.*。从 2.7 升级到 3.0 时,配置没改会连到默认的 localhost:6379,启动不报错但连不上。

spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379
      password: change-me
      database: 0
      timeout: 3s
      lettuce:
        pool:
          max-active: 16
          max-idle: 8
          min-idle: 2

如果用的是 Spring Boot 2.7,把 spring.data.redis 换成 spring.redis 即可。

序列化的选择

RedisTemplate 默认使用 JDK 序列化,存进去的 key 和 value 都是二进制,用 redis-cli 看是一堆乱码。跨语言、跨服务读取时非常不方便。

更常见的做法是 key 用字符串、value 用 JSON:

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    GenericJackson2JsonRedisSerializer serializer =
            new GenericJackson2JsonRedisSerializer();
    template.setKeySerializer(RedisSerializer.string());
    template.setHashKeySerializer(RedisSerializer.string());
    template.setValueSerializer(serializer);
    template.setHashValueSerializer(serializer);
    template.afterPropertiesSet();
    return template;
}

GenericJackson2JsonRedisSerializer 会写入 @class 类型信息,反序列化时能还原成原来的对象。代价是 JSON 里多一个字段,而且类名重构后会反序列化失败。如果不想暴露类名,可以用 Jackson2JsonRedisSerializer 指定具体类型,或者直接用 StringRedisTemplate 存 JSON 字符串,由业务自己序列化。

选择哪种方式,主要看使用方。只在 Java 服务内部读写、类结构稳定,用带类型信息的 JSON 省事;需要给其他语言的服务或脚本读取,建议用 StringRedisTemplate 存纯 JSON,把序列化规则留在业务层,数据结构更可控,跨语言也不会因为 @class 字段解析失败。

实体里如果有 LocalDateTime,需要给 ObjectMapper 注册 JavaTimeModule,否则读写会报 Java 8 date/time type not supported。

常见坑

1. 配置前缀写错版本

前面提过,Boot 3 必须用 spring.data.redis。升级后接口全部超时,日志里是 Unable to connect to localhost:6379,基本就是这个原因。

2. 密码含特殊字符

密码里有 @、#、: 时,写在 URL 形式的配置里需要转义。更稳妥的做法是用 host、port、password 分开配置,而不是拼一个 redis:// 地址。

3. 连接池配了没生效

只加了 starter,没加 commons-pool2,池化参数全部被忽略。压测时连接数上不去,就是这个原因。

4. 键名没有统一前缀

不同业务往同一个库塞 key,排查时根本分不清谁是谁。建议统一成 业务:模块:标识 的格式,比如 order:detail:1001,并给 key 设置合理的过期时间。

5. 用 keys * 查数据

keys * 会阻塞 Redis,生产环境数据量大时直接卡住。用 SCAN 游标遍历,或者通过业务前缀 + SCAN MATCH 查询。

6. 存了大对象

把一个几百 KB 的列表整体塞进一个 key,每次读写都要传输和反序列化,网络和 CPU 都吃不消。大 key 要拆分,或者改用 Hash 分字段存。

7. 缓存和数据库双写不一致

先更新数据库再删缓存、先删缓存再更新数据库,两种顺序都有并发问题。这个话题展开比较长,核心原则是:缓存只做加速,关键数据以数据库为准,删除缓存失败要有补偿。

排查步骤

连不上 Redis 时,按"网络—认证—序列化"的顺序排查:

  1. 容器里 redis-cli ping 是否正常,确认 Redis 本身活着;
  2. 宿主机 telnet 127.0.0.1 6379 或 nc -zv,确认端口通;
  3. 看应用日志里的异常类型:连接拒绝、认证失败、超时,对应的问题完全不同;
  4. 用 redis-cli -a 密码 info 看 connected_clients、used_memory、keyspace_hits;
  5. 序列化问题看 redis-cli get key 的内容,乱码说明还在用 JDK 序列化。
redis-cli -h 127.0.0.1 -p 6379 -a change-me info memory
redis-cli -h 127.0.0.1 -p 6379 -a change-me slowlog get 10

slowlog 能看出有没有慢命令,info memory 能看出内存是否接近上限。把这两条养成习惯,很多问题在变成故障之前就能发现。

参考

评论

推荐阅读

订阅邮件

订阅更新,及时收到最新文章。

作者