Java 客户端 Jedis/Lettuce 实战
本教程共 40 篇 · 第 33 篇 · 更新于 2026-08-02
33. Java 客户端 Jedis/Lettuce 实战
命令行 redis-cli 适合学习和调试,但真实项目里你几乎总是用编程语言客户端来操作 Redis。Java 生态里最主流的两个官方推荐客户端是 Jedis 和 Lettuce:Jedis 轻量、API 直观、线程不安全(通常配合连接池使用);Lettuce 基于 Netty,线程安全,天然支持同步、异步与响应式,并且内建连接池与集群/哨兵支持。本章码上学带你把这两个客户端的「连接 → 基本读写 → 批处理」三段式跑通,并重点演示 Lettuce 的异步能力。
提示:下面先用
redis-cli把要做的操作演示一遍,后续 Java 代码的效果与这些命令完全等价。
127.0.0.1:6379> SET tutorial:name "Redis tutorial"
OK
127.0.0.1:6379> GET tutorial:name
"Redis tutorial"
127.0.0.1:6379> LPUSH tutorial:list Redis Mongodb Mysql
(integer) 3
127.0.0.1:6379> LRANGE tutorial:list 0 -1
1) "Mysql"
2) "Mongodb"
3) "Redis"
本节目标
- 能为 Java 项目引入 Jedis 与 Lettuce 依赖,并建立到 Redis 8.x 的连接。
- 用 Jedis 完成字符串、列表的读写,以及
KEYS遍历等基本操作。 - 理解 Jedis 的管道(Pipeline)如何一次性批处理多条命令、减少网络往返。
- 用 Lettuce 完成同步读写,并掌握其
RedisAsyncCommands异步调用模型。 - 清楚 Jedis 与 Lettuce 在连接方式、线程模型上的取舍,能按场景选型。
一、Jedis:轻量直观的同步客户端
引入依赖
Maven 项目在 pom.xml 中加入(版本号以 Maven Central 最新稳定版为准):
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.2.0</version>
</dependency>
连接并读写
Jedis 的连接方式非常直白:指定主机与端口即可。如果 Redis 设置了密码,再调用 auth。
import redis.clients.jedis.Jedis;
public class RedisStringJava {
public static void main(String[] args) {
// 连接本地 Redis 服务(默认端口 6379)
try (Jedis jedis = new Jedis("localhost")) {
// 若设置了密码,取消下一行注释
// jedis.auth("your-password");
System.out.println("连接成功");
// 写入字符串
jedis.set("tutorial:name", "Redis tutorial");
// 读取并打印
System.out.println("存储的字符串为: " + jedis.get("tutorial:name"));
// 写入列表(从左侧压入)
jedis.lpush("tutorial:list", "Redis", "Mongodb", "Mysql");
// 取出全部元素
System.out.println("列表内容: " + jedis.lrange("tutorial:list", 0, -1));
}
}
}
运行输出:
连接成功
存储的字符串为: Redis tutorial
列表内容: [Mysql, Mongodb, Redis]
注意两点:第一,try (Jedis jedis = ...) 用 try-with-resources 自动关闭连接,避免连接泄漏;第二,列表用 LPUSH 从左边压入,所以 LRANGE 0 -1 看到的是「后压入的在前」,这与 redis-cli 表现一致。
遍历键
KEYS * 在生产环境要慎用(会阻塞),学习阶段可以这样看当前有哪些键:
import redis.clients.jedis.Jedis;
import java.util.Set;
public class RedisKeyJava {
public static void main(String[] args) {
try (Jedis jedis = new Jedis("localhost")) {
Set<String> keys = jedis.keys("*");
keys.forEach(System.out::println);
}
}
}
管道 Pipeline:一次往返发多条命令
如果需要连续执行很多命令,每发一条等一条回复,网络往返(RTT)会成为瓶颈。Jedis 的 pipelined() 可以把多条命令打包,最后一次性同步:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.util.List;
public class RedisPipelineJava {
public static void main(String[] args) {
try (Jedis jedis = new Jedis("localhost")) {
Pipeline p = jedis.pipelined();
for (int i = 0; i < 1000; i++) {
p.set("item:" + i, String.valueOf(i));
}
// 一次性提交并取回所有结果
List<Object> results = p.syncAndReturnAll();
System.out.println("共执行命令: " + results.size());
}
}
}
p.syncAndReturnAll() 返回每条命令的回复列表;如果你不需要关心返回值,用 p.sync() 即可。这把 1000 次网络往返压缩成了 1 次,吞吐量差异巨大。
二、Lettuce:线程安全的异步/响应式客户端
引入依赖
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.4.0</version>
</dependency>
同步读写
Lettuce 的连接对象 StatefulRedisConnection 是线程安全的,整个应用可以共用一个,通过它获取同步命令接口:
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
public class LettuceSync {
public static void main(String[] args) {
// 用 Redis URI 创建客户端,含密码可写为 redis://:password@localhost:6379
RedisClient client = RedisClient.create("redis://localhost:6379");
try (StatefulRedisConnection<String, String> connection = client.connect()) {
RedisCommands<String, String> commands = connection.sync();
commands.set("tutorial:name", "Redis tutorial");
System.out.println("存储的字符串为: " + commands.get("tutorial:name"));
commands.lpush("tutorial:list", "Redis", "Mongodb", "Mysql");
System.out.println("列表内容: " + commands.lrange("tutorial:list", 0, -1));
}
client.shutdown();
}
}
与 Jedis 不同,这里 connection 可以安全地被多个线程共享;每个线程通过 connection.sync() 拿到自己的命令接口即可,不需要为每个请求新建连接。
Lettuce 异步:RedisAsyncCommands
Lettuce 的强项是异步。通过 connection.async() 拿到 RedisAsyncCommands,每个方法返回一个 RedisFuture(本质是 CompletableFuture 的子类),调用后立即返回,不阻塞当前线程:
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class LettuceAsync {
public static void main(String[] args) throws ExecutionException, InterruptedException {
RedisClient client = RedisClient.create("redis://localhost:6379");
try (StatefulRedisConnection<String, String> connection = client.connect()) {
RedisAsyncCommands<String, String> async = connection.async();
// 发起异步写入,立即返回 Future,不阻塞
Future<String> setFuture = async.set("tutorial:name", "Redis tutorial");
Future<String> getFuture = async.get("tutorial:name");
// 真正需要结果时再等待
System.out.println("SET 结果: " + setFuture.get());
System.out.println("GET 结果: " + getFuture.get());
}
client.shutdown();
}
}
更常见的写法是链式回调,而不是同步 get(),这样才能发挥异步的价值:
async.set("counter", "1")
.thenCompose(v -> async.incr("counter"))
.thenAccept(value -> System.out.println("自增后: " + value))
.toCompletableFuture()
.join();
此外,Lettuce 还提供 connection.reactive() 返回 RedisReactiveCommands,基于 Project Reactor 做响应式编程,适合高并发流式场景(如 WebFlux)。对大多数业务,同步接口或 async 接口已足够。
三、连接池与生产细节(JedisPool)
前面演示的「每次 new Jedis()」只为讲清 API,生产环境绝不能这么写:Jedis 实例线程不安全,且每次新建都要经历 TCP 握手与协议协商,开销很大。正确做法是用 JedisPool 复用连接,多线程各自从池里借、用完归还。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(64); // 池中最大连接数
config.setMaxIdle(16); // 最大空闲连接
config.setMinIdle(4); // 最小空闲连接(预热)
config.setMaxWaitMillis(2000); // 借不到时最多等 2 秒
try (JedisPool pool = new JedisPool(config, "localhost", 6379)) {
try (Jedis jedis = pool.getResource()) { // 从池借出
jedis.set("k", "v");
System.out.println(jedis.get("k"));
} // 归还,不是关闭
}
若 Redis 8.x 启用了 ACL(见第 36 章),连接时要带上用户名:jedis.auth("username", "password"),而不是只传密码。把这个池对象声明为全局单例、整个应用共享即可,无需每个请求都新建。
四、选型建议
- Jedis:API 简单、文档多、上手快,适合中小型项目或快速验证。注意它是线程不安全的,多线程下要通过连接池(见第 35 章)分配连接。
- Lettuce:线程安全、内建连接池、天然支持异步/响应式与集群/哨兵,适合高并发、响应式技术栈(Spring Boot 的
spring-boot-starter-data-redis默认就是 Lettuce)。
提示:码上学建议新项目优先选 Lettuce;如果团队已经重度使用 Jedis 且不想改,配合连接池也完全能扛住生产流量,不必强行迁移。
小结
- Jedis 与 Lettuce 是 Java 连接 Redis 8.x 的两个官方推荐客户端,都遵循「连接 → 读写 → 批处理」的基本范式。
- Jedis 轻量直观、
try-with-resources即可自动关闭;批量操作请使用pipelined()减少 RTT。 - Lettuce 的连接对象线程安全,支持
sync()同步、async()异步、reactive()响应式三种模式。 - 异步场景下用
RedisFuture链式组合命令,避免在主线程get()阻塞,才能吃到异步红利。
常见误区
- 把 Jedis 实例放到成员变量里被多线程共用。Jedis 线程不安全,共享同一个实例会出现诡异的数据串扰;要么每个线程独立获取(配合连接池),要么直接用 Lettuce。
- 用了异步客户端却在每步都
future.get()同步等待。这样等于把异步又变回同步,既没提升吞吐,还多了 Future 开销。 - 在生产代码里用
KEYS *遍历键。它会遍历全库并阻塞事件循环,正确做法是用SCAN游标分批扫描。
提示:官方 redis.io 下载页另将 8.8 标注为 “Latest stable”,而 GitHub 上的最新发布 tag 为 8.10.0;二者同属 8.x,客户端调用层面差异对本教程影响极小,本教程统一以 Redis 8.x(最新稳定版)表述。