首页 / Redis 入门教程 / Java 客户端 Jedis/Lettuce 实战

Redis 入门教程

Java 客户端 Jedis/Lettuce 实战

本教程共 40 篇 · 第 33 篇 · 更新于 2026-08-02

JavaJedisLettuce客户端异步

33. Java 客户端 Jedis/Lettuce 实战

命令行 redis-cli 适合学习和调试,但真实项目里你几乎总是用编程语言客户端来操作 Redis。Java 生态里最主流的两个官方推荐客户端是 JedisLettuce: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(最新稳定版)表述。