选 Redis 客户端,本质上是在"命令可预测性"和"连接模型"之间做权衡。Java 生态里最早普及的 Jedis 是同步阻塞的,一个实例同时只能服务一个线程,生产必须靠连接池;后来 Lettuce 基于 Netty,默认线程安全、连接可复用,成了 Spring Boot 2 之后的默认选择。但换了客户端不等于解决了分布式锁——Redlock 试图在多实例上保证互斥,却因时钟、GC、重启等现实问题引来争议。本文把三篇笔记串成一条线:先看清两个客户端的差异与各自坑点,再谈分布式锁到底在保什么。
一、Jedis:同步阻塞,生产必须走连接池
Jedis 是 Java 里最早普及的 Redis 客户端,核心特点是调用是同步阻塞的:一个 Jedis 实例同时只能被一个线程使用。生产环境必须走连接池 JedisPool,用完归还,否则会泄连接或把带状态的连接交给下一个请求。它对 Redis 命令的包装非常直白,调试时能直接对应 redis-cli。集群模式用 JedisCluster,但 slot 迁移、MOVED/ASK 重定向和连接数爆炸都要自己关注。新项目更常选 Lettuce 或 Redisson,Jedis 仍适合老代码维护和对命令序列要求严的脚本场景。
三个容易踩的坑
new Jedis()直连上线:一个实例只能单线程用,并发下互相踩。统一走JedisPool,try-with-resources或finally里returnResource,禁止跨线程复用同一实例。- 混淆 pipeline 与 transaction:管道只减少 RTT,不保证原子性;多键一致性要用
MULTI/EXEC或 Lua。 - 集群下跨 slot 做事务:会失败。同一业务对象用 hash tag 锁定 slot;超时、最大等待和测试时间要配到与业务 SLA 相符。
什么时候用:当你维护早期 Spring Data Redis 项目、或需要一对一命令映射以便复现线上问题时,Jedis 仍然直接。高并发、响应式栈和要跨线程复用连接的新服务,应优先考虑 Lettuce。分布式锁、延迟队列这类高层抽象不要用 Jedis 手拼,用 Redisson 更省心。
二、Lettuce:基于 Netty 的线程安全客户端
Lettuce 是基于 Netty 的 Redis 客户端,默认线程安全且可以连接复用。它提供同步 API 与基于 Reactor 的异步 API,Spring Boot 2 以后默认就是它。与 Jedis 相比,不用每个线程借还实例,但要理解共享连接上的命令竖线与 pub/sub 占用。集群、传送、SSL 和超时都在 ClientOptions 与 ClusterClientOptions 里配。异步链路下要避免在阻塞方法里调 reactive API,否则线程模型会乱。
三个容易踩的坑
- 分不清
StatefulRedisConnection与连接池:单连接多线程复用时命令会交织;事务、阻塞脚本要独占连接或用池。 - 忽略断线重连拓扑:默认
auto-reconnect在集群折叠时可能打到旧节点,要配 topology refresh,明确缓存、超时与重连策略。 - 与 Spring Cache、Redisson 混用同一连接对象:
Lettuce负责命令通道,分布式锁仍建议交给专门库,不要混用同一连接。
什么时候用:新建 Spring Boot 服务、需要跨线程共享连接或要 WebFlux 异步访问 Redis 时,优先选 Lettuce。极致追求命令级可预测性、或老项目已经把 Jedis 连接池调稳的,不必为了换库而换。若需要红锁、延迟队列、布隆过滤器,Lettuce 只当底层通道,业务语义用 Redisson。
三、Redlock 分布式锁及其争议:看清它到底在保什么
Redis Redlock 是在多个独立 Redis 实例上多数派加锁的算法:向半数以上节点用同一随机值 SET NX PX,全部成功才算拿到锁。它试图在主从复制延迟下仍保持互斥。Martin Kleppmann 指出时钟回拨、长时间 GC 和实例重启会让锁失效而业务还在临界区。工程上更稳妥的是单 Redis + fencing token,或把锁下沉到数据库与协调服务。理解 Redlock 的价值是看清分布式锁到底在保证什么。
三个容易踩的坑
- 释放锁用
DEL:会删掉别人刚拿到的锁。锁必须带随机值,释放时用 Lua 比对 value,禁止DEL错别人的锁。 - 业务超过 TTL 还在写:拿到锁后业务必须在 TTL 内完成,或用续期。GC 停顿超过 TTL 就要主动放弃,不能继续写。
- 把单实例锁当跨机房强一致:单实例 Redis 锁适合任务去重,不要把它吹成跨机房强一致。需要真正互斥时加 fencing token:每次加锁递增版本,存储层拒绝旧版本写入。
什么时候用:多个 Redis 独立部署、只能用 Redis 做协调、且能接受极小窗口的重复进入时,可以考虑 Redlock。账务、库存扣减这类不能错的场景,优先用数据库约束或 ZooKeeper/etcd。单实例 Redis 锁适合任务去重,不要把它吹成跨机房强一致。
附:Redis 客户端与锁方案对比表
| 场景 | 用什么 | 不要用什么 |
|---|---|---|
| 老项目维护、需一对一命令映射复现问题 | Jedis + JedisPool |
new Jedis() 直连、跨线程复用同一实例 |
| 高并发、跨线程复用连接、WebFlux 异步 | Lettuce(默认线程安全) |
为换库而换、把 Jedis 池调稳的硬迁 |
| 红锁 / 延迟队列 / 布隆过滤器等高层语义 | Lettuce 当通道 + Redisson 做业务 |
用 Jedis/Lettuce 手拼分布式锁 |
| 多实例强一致加锁 | Redlock(接受极小重复窗口) |
单实例锁吹成跨机房强一致 |
| 账务 / 库存等不能错的互斥 | 数据库约束 / ZooKeeper / etcd | Redlock 或单实例锁硬扛 |
| 任务去重、弱一致保护 | 单实例 Redis 锁(带随机值 + Lua 释放) | 用 DEL 释放、业务超 TTL 继续写 |
Redis 客户端与分布式锁:Jedis、Lettuce 与 Redlock 的取舍
https://lautung.com/archives/redis-client-and-lock
评论