dot Redis 8 来了——并且它是开源的

了解更多

Jedis vs. Lettuce:探索

我骨子里是个探险家,所以当必须做出技术决策时——比如选择一个 Redis 客户端——我就会深入探索。以下是我对这对押韵的 Java 客户端 JedisLettuce 进行探索的记录。

让我们从基础开始,分别检查这两个客户端,以了解它们之间的差异。

什么是 Lettuce?

Lettuce 是一个完全非阻塞的 Redis Java 客户端。它支持同步和异步通信。其复杂的抽象允许您轻松扩展产品。

可以将 Lettuce 视为一个更高级的客户端,支持集群、Sentinel、管道和编解码器。

什么是 Jedis?

Jedis 是 Redis 内部的一个客户端库,旨在提高性能和易用性。与其他 Redis Java 客户端相比,Jedis 更轻量;它提供的功能较少,但仍然可以处理大量内存。

由于其功能更简单,Jedis 更易于使用,但它只能同步地与集群一起工作。如果您选择 Jedis,您可能会发现更容易专注于应用程序和数据,而不是数据存储机制。

追求利润的箴言式目标,就像内裤一样,总是在那里。 但您能从中受益的部分是选择标准。它将帮助我们决定何时 Jedis 是正确的选择,以及何时 Lettuce 是最佳方案。这非常重要,因为我们都知道,在选择工具时,对任何问题的回答都是“视情况而定”。

我的比较计划很简单

  • 在代码中尝试一些简单的事情
  • 在代码中尝试一些高级的事情
  • 得出某种选择标准
  • 利润!

一种简单的代码

为了弄清楚 Jedis 与 Lettuce 之争,让我们比较一下最简单操作的代码:从一个 Redis 实例设置和获取值。

首先,我们使用 Jedis 来实现

package com.guyroyse.blogs.lettucevsjedis;

import redis.clients.jedis.Jedis;

public class JedisSetGet {

    private static final String YOUR_CONNECTION_STRING = "redis://:foobared@yourserver:6379/0";

    public static void main(String[] args) {

        Jedis jedis = new Jedis(YOUR_CONNECTION_STRING);

        jedis.set("foo", "bar");
        String result = jedis.get("foo");

        jedis.close();

        System.out.println(result); // "bar"
    }
}

看看代码,这非常简单。创建一个连接。使用它。关闭它。

接下来,我们使用 Lettuce 来实现

package com.guyroyse.blogs.lettucevsjedis;

import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class LettuceSetGet {

    private static final String YOUR_CONNECTION_STRING = "redis://:foobared@yourserver:6379/0";

    public static void main(String[] args) {
        RedisClient redisClient = RedisClient.create(YOUR_CONNECTION_STRING);
        StatefulRedisConnection<String, String> connection = redisClient.connect();
        RedisCommands<String, String> sync = connection.sync();

        sync.set("foo", "bar");

        String result = sync.get("foo");

        connection.close();
        redisClient.shutdown();

        System.out.println(result); // "bar"
    }
}

这看起来稍微复杂一些。有一个 Java 客户端、一个连接和一个命令对象。它们的名称和模板性质表明它们可能有多种变体。也许除了 StatefulRedisConnection<String, String> 类型之外,我们还有接受 byte[]? 的无状态变体?(剧透:对于集群和主/从配置有多种连接类型,但没有无状态的。)

然而,一旦您完成了设置和拆除,这两种客户端的基本代码都是相同的:创建一个连接。使用它。关闭它。

目前,对于像这样简单的操作,Jedis 看起来更容易。这很有道理,因为它代码量更少。但我确信 Lettuce 软件提供所有这些东西是有原因的——很可能是为了处理更高级的场景。

Jedis 和多线程代码

Jedis 可以很好地处理多线程应用程序,但 Jedis 连接不是线程安全的。所以不要共享它们。如果您跨线程共享 Jedis 连接,Redis 会报告各种协议错误,例如

应为 '$' 但得到 ' '

为了解决这类问题,请使用 JedisPool——一个分发线程不安全 Jedis 对象的线程安全对象。使用它就像使用 Jedis 的其他部分一样简单。只需请求一个线程并在完成后通过 .close() 将其返回到连接池即可。下面是它的实际应用

package com.guyroyse.blogs.lettucevsjedis;

import redis.clients.jedis.*;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class JedisMultithreaded {

    private static final String YOUR_CONNECTION_STRING = "redis://:foobared@yourserver:6379/0";

    public static void main(String[] args) {
        JedisPool pool = new JedisPool(YOUR_CONNECTION_STRING);

        List<String> allResults = IntStream.rangeClosed(1, 5)
                .parallel()
                .mapToObj(n -> {
                    Jedis jedis = pool.getResource();

                    jedis.set("foo" + n, "bar" + n);
                    String result = jedis.get("foo" + n);

                    jedis.close();

                    return result;
                })
                .collect(Collectors.toList());

        pool.close();

        System.out.println(allResults); // "bar1, bar2, bar3, bar4, bar5"
    }
}

每个 Jedis 对象都封装了一个到 Redis 的单个连接,因此(取决于连接池的大小)可能存在阻塞或空闲连接。此外,这些连接是同步的,因此总是存在一定程度的空闲。

Jedis、Lettuce 和集群

我觉得我应该谈谈集群,但至少在比较方面没什么好说的。有很多功能可以讨论,但这两个库都支持集群。毫不意外的是,Jedis 更易于使用,但它只能同步地与集群一起工作。Lettuce 软件使用起来更困难,但能够以同步、异步和响应式方式与集群交互。

这是反复出现的主题。这并不令人惊讶。正如它自己承认的,“Jedis 的设计理念是‘易于使用’。”而 Lettuce 则在其主页上写道:“Lettuce 是一个可扩展的 Redis 客户端,用于构建非阻塞响应式应用程序。”

当然,如果您使用Redis Enterprise,您不必担心集群问题,因为它由服务器端处理。只需使用 Jedis 或 Lettuce 的非集群 API,管理好您的键以确保它们分配到正确的 shard,然后就可以正常使用了。