Redis Java 客户端:我是否需要将我的命令缓冲到管道中以提高性能?

Redis Java Client: Do I need to buffer my commands into a pipeline for performance?

所以我只是递增排序集中的分数。这是我 运行 唯一的命令,每秒大约 10-30 个命令,来自 Java 应用程序,使用 Jedis 客户端。由于我只是更新分数,所以我也不关心响应。我担心的是每个 ZINCRBY 命令都被放入它自己的 TCP 数据包中,并且在允许我的线程发送下一个 ZINCRBY 线程之前等待下一个回复。

所以,我只想实现流水线来一次批量说出 50 个命令。这是我看到 code/design-pattern 气味的地方:这种设计模式是否足够普遍以至于驱动程序应该处理它?看起来 .net "StackExchange.redis" 驱动程序会自动执行命令批处理,但 Java 驱动程序没有此功能?我的想法是制作一个自定义的 Redis 命令缓冲区 class,它将传入的命令放入管道并在 50 个项目后调用 sync(),真的需要吗?

此外,我在日志中注意到了这一点,因为我通过 Spring Data Redis 使用 Jedis:

20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.394 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.394 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.629 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.630 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.630 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection

所以看起来它正在关闭每个天真执行的命令的连接(通过 Spring 提供的模板模式)。我认为关闭连接会强制 TCP 缓冲区为每个数据包发送一个命令,所以这对我来说似乎效率很低,因为套接字占用了相当数量的 CPU。尽管 Spring Data Redis API 确实允许直接访问 Jedis 客户端,并且如果管道当前打开则不会关闭连接,因此编写 "pipeline buffer" 是一个选项。

简而言之,我应该create/leverage一个写入redis管道并在X命令后刷新的缓冲区吗?我只是不喜欢天真地浪费所有这些 CPU 周期(更高的 AWS 账单)运行 每个命令的想法,并且很好奇我的场景是否有更好的设计模式。或者,如果切换到不同的 Java Redis 客户端将解决此问题。或者如果有一些 Java 库已经在 redis 管道中缓冲命令。

流水线是 Redis 的一种常见模式,用于降低通信成本。 Jedis is a recommended Java driver for redis, which supports pipelining. Lettuce is an alternative, which supports pipelining 也是。

我认为我们需要在这里更深入地了解细节,因为您在这里提到了不同的方面。

一般来说,所有 Java 个 Redis 客户端(Jedis, Lettuce, Redisson 以及更多)默认情况下直接将命令写入 TCP 通道。因此,每个命令都作为一个或多个 TCP 数据包发送。 Lettuce 和 Redisson 作为它们的操作模式,因为这两个客户端都在后台使用 asynchronous/event-driven 编程模型将 Redis 命令写入 TCP 通道。 Jedis 强制您等待命令结果,因为它公开了阻塞 API。 Redisson 和 Lettuce 公开了不同类型的 API(异步使用 Futures 或反应式使用 RxJava/Reactive Streams),它们不会强制您等待命令结果。

Batching/buffering 是另一种在客户端内存中收集命令并将命令作为批处理发送到 Redis 的技术。这适用于 Lettuce and Redisson 客户端。 Jedis 以其流水线模式将命令直接写入 TCP 通道。

命令批处理带来了一些影响。可以自动批处理命令(比如大小为 10 或 50),但这需要用户注意。批处理总是需要一些最终同步,以避免命令在队列中逗留而未发送,因为尚未达到批处理大小。

Spring Data Redis 使用Jedis 和Lettuce 来暴露其功能,因此Spring Data Redis 需要应对这两个驱动程序的共同点。您可以设置 Spring Data Redis 以与 Jedis 一起使用连接池,这样您就可以从每次与 Redis 交互时都不会关闭的池连接中获益。