【入门篇】1.5 redis 客户端Jedis和Lettuce对比详解

文章目录

  • 0. 前言
  • 1. Lettuce 与Jedis 对比
  • 2.详解
    • 2.1 线程安全
    • 2.2 阻塞/非阻塞
    • 2.3 集群支持
    • 2.4 PUB/SUB模型
    • 2.5 二进制协议
    • 3. 参考资料

      0. 前言

      对于Java开发者来说,Jedis和Lettuce是两种非常常见的Redis客户端,他们可以帮助开发者更容易地在Java应用中使用Redis。然而,这两种客户端在设计和实现上有着许多不同之处,这就需要深入理解它们的差异,以便根据自己的需求做出合适的选择。

      在这篇博客中,将详细讲解Jedis和Lettuce这两种Redis客户端的特点和区别。首先,将介绍他们的基本特性,包括线程安全性、连接管理方式、API设计等。然后,将深入比较他们在处理Redis二进制协议时的差异。最后,将通过一些具体的示例,来展示如何在Java应用中使用Jedis和Lettuce。

      1. Lettuce 与Jedis 对比

      维度/库LettuceJedis
      单线程/多线程Lettuce是基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,应该被用作长期存在的线程安全对象。Jedis实例不是线程安全的,因此在多线程环境下,你需要每个线程创建一个新的连接实例,或者使用连接池。
      阻塞/非阻塞Lettuce支持异步、反应式、同步和非阻塞操作。Jedis操作是同步阻塞的,不支持异步和非阻塞操作。
      集群支持Lettuce提供了Redis Cluster的原生支持。Jedis也支持Redis Cluster,但需要手动处理重定向。
      PUB/SUB模型Lettuce支持发布-订阅模型。Jedis也支持发布-订阅模型。
      二进制协议Lettuce直接使用Netty来处理命令和结果,可以处理任何Redis协议和命令。Jedis使用自己的协议处理器,对协议的支持可能不太完整。
      项目活跃度Lettuce是目前最活跃的Redis Java客户端项目,一直在持续更新和添加新特性。Jedis的活跃度较低,更新和新功能的添加较慢。
      连接池Lettuce的连接实例是线程安全的,大多数情况下,你不需要使用连接池。不过,Lettuce也提供了一个可选的内置连接池。在多线程环境下,你需要使用Jedis的连接池来管理和复用连接。
      依赖Lettuce依赖于Netty。Jedis没有外部依赖。
      事务Lettuce支持Redis的事务。Jedis也支持Redis的事务。
      SentinelLettuce提供了对Redis Sentinel的原生支持。Jedis也支持Redis Sentinel。

      2.详解

      2.1 线程安全

      Lettuce是基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,应该被用作长期存在的线程安全对象。 Jedis实例不是线程安全的,因此在多线程环境下,你需要每个线程创建一个新的连接实例,或者使用连接池。 举例说明

      Lettuce和Jedis的线程安全性是它们的一个主要区别。

      Lettuce是基于Netty的,Netty是一个多线程的,事件驱动的I/O框架。这意味着Lettuce可以处理多个并发连接,因此它是线程安全的。例如,如果你有一个应用程序,它在多个线程中并发地访问Redis,你可以创建一个Lettuce连接实例,并且所有的线程都可以使用这个连接实例。这是因为Lettuce内部会处理并发访问,保证数据的一致性。

      例如:

      RedisClient redisClient = RedisClient.create("redis://localhost:6379/0");
      StatefulRedisConnection connection = redisClient.connect();
      // 在多个线程中使用同一个连接
      new Thread(() -> { RedisCommands commands = connection.sync();
          commands.set("key", "value");
      }).start();
      new Thread(() -> { RedisCommands commands = connection.sync();
          String value = commands.get("key");
      }).start();
      

      在这个例子中,在两个线程中使用了同一个Redis连接。

      而Jedis不是线程安全的。这意味着,如果你试图在多个线程中并发地使用同一个Jedis实例,可能会导致数据混乱、错误或其他未预期的问题。在多线程环境下,你需要为每个线程创建一个新的Jedis实例,或者使用连接池。

      例如:

      JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
      // 在每个线程中创建一个新的Jedis实例
      new Thread(() -> { try (Jedis jedis = pool.getResource()) { jedis.set("key", "value");
          }
      }).start();
      new Thread(() -> { try (Jedis jedis = pool.getResource()) { String value = jedis.get("key");
          }
      }).start();
      

      2.2 阻塞/非阻塞

      Lettuce和Jedis在阻塞/非阻塞操作方面的差异主要体现在它们支持的操作类型上。

      Lettuce支持异步、同步和反应式的操作。这是因为它基于Netty,一个为了网络应用设计的多线程、事件驱动的框架。这也意味着你可以使用Lettuce实现非阻塞的操作。

      例如,你可以使用Lettuce实现异步操作:

      RedisClient redisClient = RedisClient.create("redis://localhost:6379/0");
      StatefulRedisConnection connection = redisClient.connect();
      RedisAsyncCommands asyncCommands = connection.async();
      RedisFuture futureValue = asyncCommands.get("key");
      futureValue.thenAccept(System.out::println);
      

      在这个例子中,异步地从Redis获取一个值,并且一旦值可用,就打印它(这是一个非阻塞操作)。

      然而,Jedis只支持同步阻塞的操作。这意味着,在操作完成之前,线程将被阻塞。Jedis不支持异步和非阻塞操作。

      例如,当你使用Jedis进行操作时:

      Jedis jedis = new Jedis("localhost", 6379);
      String value = jedis.get("key");
      System.out.println(value);
      

      在这个例子中,从Redis获取一个值,这是一个阻塞操作,因为在值返回之前,线程将被阻塞。

      2.3 集群支持

      Lettuce提供了Redis Cluster的原生支持。 Jedis也支持Redis Cluster,但需要手动处理重定向。

      Lettuce和Jedis在对Redis Cluster的支持方面也存在一些差异。

      Lettuce提供了对Redis Cluster的原生支持。它可以自动处理集群中节点的更改,包括从节点到主节点的故障转移,并且在执行命令时自动处理重定向。这是通过周期性地获取集群的当前状态来实现的。

      例如,你可以使用Lettuce连接Redis Cluster并执行操作:

      RedisClusterClient clusterClient = RedisClusterClient.create("redis://localhost:7379");
      StatefulRedisClusterConnection connection = clusterClient.connect();
      RedisAdvancedClusterAsyncCommands asyncCommands = connection.async();
      asyncCommands.set("key", "value");
      

      在这个例子中,连接到Redis Cluster并设置一个键值对,Lettuce会自动处理任何由于节点更改而发生的重定向。

      另一方面,Jedis也支持Redis Cluster,但是你需要手动处理重定向。这意味着,如果一个操作被重定向到另一个节点,你需要捕获JedisMovedDataException异常,然后重新在正确的节点上执行操作。

      例如:

      JedisCluster jedisCluster = new JedisCluster(new HostAndPort("localhost", 7379));
      try { jedisCluster.set("key", "value");
      } catch (JedisMovedDataException e) { HostAndPort newHost = e.getTargetNode();
          JedisCluster newJedisCluster = new JedisCluster(newHost);
          newJedisCluster.set("key", "value");
      }
      

      在这个例子中,试图在Redis Cluster中设置一个键值对,如果的操作被重定向到另一个节点,需要捕获异常并在新的节点上重新执行操作。

      2.4 PUB/SUB模型

      Lettuce和Jedis都支持Redis的发布-订阅(Pub/Sub)模型,以下是他们的一些区别:

      1. Thread Safety:Lettuce的连接实例是线程安全的,因此你可以在多个线程中共享同一个连接实例,而不需要担心线程安全问题。而Jedis实例不是线程安全的,所以在实现发布-订阅模型时,你需要为每个线程创建一个新的Jedis实例。

      2. Connection Management:Lettuce使用Netty框架进行网络通信,它可以通过一切异步和同步的通信方式进行连接管理。这意味着在复杂的发布-订阅场景中,Lettuce可能会有更好的性能。而Jedis使用的是Java的标准网络库,它的连接管理是基于阻塞的I/O操作,这会在大量并发操作时影响性能。

      3. API Design:Lettuce的API设计更现代,使用起来可能更加方便。例如,它提供了完全基于Future的异步执行模型,让你能够更好地控制异步操作。而Jedis的API设计比较传统,虽然也支持异步操作,但可能不如Lettuce灵活。

      例如,Lettuce的Pub/Sub示例:

      RedisClient redisClient = RedisClient.create("redis://localhost:6379");
      StatefulRedisPubSubConnection pubSubConnection = redisClient.connectPubSub();
      pubSubConnection.addListener(new RedisPubSubListener<>(){...});
      pubSubConnection.async().subscribe("channel");
      

      Jedis的Pub/Sub示例:

      Jedis jedis = new Jedis("localhost");
      jedis.subscribe(new JedisPubSub() {...}, "channel");
      

      2.5 二进制协议

      Lettuce直接使用Netty来处理命令和结果,可以处理任何Redis协议和命令。 Jedis使用自己的协议处理器,对协议的支持可能不太完整。

      二进制协议是在网络中传输数据的一种方式,它将所有数据都转化为二进制格式进行传输。二进制协议相对于文本协议来说,能更有效地利用网络带宽,同时也能更好地保证数据的完整性。

      Lettuce是一个基于Netty的Redis客户端,Netty是一个高效的网络框架,支持各种协议,包括TCP/IP,UDP等,也包括二进制协议。Lettuce使用Netty来处理发送到Redis服务器的命令和从Redis服务器接收的结果。因为Netty的协议支持非常完善,所以Lettuce可以处理任何Redis协议和命令,包括二进制协议。

      Jedis则使用了自己的协议处理器,虽然它也支持二进制协议,但是由于其处理器的设计可能没有Netty那么全面,对某些协议的支持可能不太完整。

      这些差异可能会在一些复杂的使用场景中产生影响。例如,在处理大量数据,或者需要使用特定Redis命令的情况下,Lettuce可能会有更好的表现。然而,在大多数常见的使用场景下,这些差异可能并不明显。

      3. 参考资料

      Lettuce

      1. 官方文档:Lettuce
      2. GitHub:Lettuce GitHub
      3. 深入了解Lettuce:Understanding Lettuce
      4. Lettuce源码分析:Lettuce source code analysis

      Jedis

      1. 官方文档:Jedis
      2. GitHub:Jedis GitHub
      3. 深入了解Jedis:Understanding Jedis
      4. Jedis源码分析:Jedis source code analysis