bigywater.tech
Spring · 100

Spring Data Redis:Lettuce、序列化器与 pipeline

RedisTemplate 存进去的 key 为什么是乱码?Lettuce 要不要配连接池?pipeline 到底快多少?Redis 挂了应用会怎样?这篇在 Spring Boot 4.0.8、Spring Data Redis 4、Lettuce 上用自己起的 Redis 逐个实测。

161 vs 42 字节
同一个 User 对象,JDK 序列化与 Jackson JSON 在 Redis 里的 value 大小
461 → 67 → 10 ms
写 10000 个 key:循环 set、executePipelined、multiSet 的中位耗时
2005 ms
Redis 宕机期间,超时设为 2 秒时每个 GET 失败前的等待时间

序列化器

实测 · 本机执行输出
  自动配置的 RedisTemplate 的 key / value 序列化器:JdkSerializationRedisSerializer / JdkSerializationRedisSerializer;StringRedisTemplate:StringRedisSerializer / StringRedisSerializer
  读回来:jdk → User[alice,30,[a, b]];json → {age=30, name=alice, tags=[a, b]}
  redis 里的 key b'json:user':value 共 42 字节,前 60 字节 b'{"age":30,"name":"alice","tags":["a","b"]}'
  redis 里的 key b'str:name':value 共 5 字节,前 60 字节 b'alice'
  redis 里的 key b'\xac\xed\x00\x05t\x00\x08jdk:user':value 共 161 字节,前 60 字节 b'\xac\xed\x00\x05sr\x00\tdemo.User4>2S\xe9\xcfg\xa5\x02\x00\x03I\x00\x03ageL\x00\x04namet\x00\x12Ljava/lang/Strin'

默认 RedisTemplate 用 JDK 序列化:key 带头、value 大、其他语言读不了;StringRedisTemplate 与 JSON 序列化器是可读的。

Lettuce 的连接

实测 · 本机执行输出
  -- classpath 上没有 commons-pool2(默认):
    Lettuce 默认的命令超时 = PT1M,连接超时 = PT10S;是否用连接池:false
    空闲时 connected_clients = 1
    64 个线程并发 GET 3 秒:共 286436 次(95479 次/秒);压测中间 connected_clients = 1
  -- classpath 上有 commons-pool2(Boot 因此自动启用连接池):
    Lettuce 默认的命令超时 = PT1M,连接超时 = PT10S;是否用连接池:true
    空闲时 connected_clients = 1
    64 个线程并发 GET 3 秒:共 285830 次(95277 次/秒);压测中间 connected_clients = 1
  -- 有 commons-pool2,再把 spring.data.redis.lettuce.pool.enabled 显式设成 false:
    Lettuce 默认的命令超时 = PT1M,连接超时 = PT10S;是否用连接池:false
    空闲时 connected_clients = 1
    64 个线程并发 GET 3 秒:共 305869 次(101956 次/秒);压测中间 connected_clients = 1
  -- 有 commons-pool2,并把 shareNativeConnection 关掉(每次操作都从池里借连接,max-active=8):
    Lettuce 默认的命令超时 = PT1M,连接超时 = PT10S;是否用连接池:true
    空闲时 connected_clients = 1
    64 个线程并发 GET 3 秒:共 163349 次(54450 次/秒);压测中间 connected_clients = 8

默认所有线程共用一个连接;有 commons-pool2 也不一定真的用池;不共享连接的池反而更慢。

pipeline

实测 · 本机执行输出
  循环里一个一个 set:10000 个 key,中位 461 ms(378~624);dbsize = 10000
  executePipelined:10000 个 key,中位 67 ms(60~121);dbsize = 10000
  multiSet(一条 MSET):10000 个 key,中位 10 ms(8~12);dbsize = 10000

往返次数是主要代价:循环 461 ms,pipeline 67 ms,MSET 10 ms。

Redis 不可用

实测 · 本机执行输出
  端口上没有 Redis:第一条命令失败,最里面的异常:Caused by: java.net.ConnectException: Connection refused
  -- 运行中把 Redis 进程 kill -9(第 2.5 秒),第 7.5 秒再启动;spring.data.redis.timeout=2s;应用每 0.5 秒 GET 一次:
      t= 0.2s GET 成功,用时 233 ms
      t= 0.7s GET 成功,用时 3 ms
      t= 1.2s GET 成功,用时 1 ms
      t= 1.7s GET 成功,用时 1 ms
      t= 2.3s GET 成功,用时 1 ms
      t= 4.8s GET 失败 QueryTimeoutException / RedisCommandTimeoutException,用时 2006 ms
      t= 7.3s GET 失败 QueryTimeoutException / RedisCommandTimeoutException,用时 2005 ms
      t= 9.8s GET 失败 QueryTimeoutException / RedisCommandTimeoutException,用时 2005 ms
      t=11.7s GET 成功,用时 1413 ms
      t=12.2s GET 成功,用时 1 ms
      t=12.7s GET 成功,用时 1 ms
      t=13.2s GET 成功,用时 1 ms
      t=13.7s GET 成功,用时 1 ms

运行中宕机时每个命令等满超时才失败,恢复后重连会让第一次调用变慢。

分步演示

Spring Data Redis
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Spring Data Redis 4.0.7、Lettuce 6.8.2、Jackson 3;Redis 8.10.2 是本机自己起的实例,端口 7390,不碰用户自己的 6379)。序列化器对比存同一个 User;连接数取自应用内 INFO clients 的 connected_clients(运行期间我的监控连接先断开);吞吐是 64 个线程循环 GET 3 秒;pipeline 每种写法 3 次取中位数;宕机实验用 kill -9 杀掉 Redis 进程再启动
    • 没有验证:Redis Cluster 与 Sentinel 下的拓扑刷新与故障转移;SSL / 认证;Lettuce 的 ClientOptions 与断线重连策略(disconnectedBehavior 等)的具体取值;ReactiveRedisTemplate;发布订阅与 Stream;Redisson 的对比;value 带类型信息(activateDefaultTyping)的 JSON 序列化器的行为;大 value 与序列化耗时
    京ICP备2026067502号-1京公网安备11010502063487号