如何在Redis中最有效地调用INCRBY和EXPIRE

dig*_*tum 3 java lua redis lettuce

对于 Redis 中可能存在或不存在的聚合对象的集合,我想对它们调用 INCRBY 和 EXPIRE。从我的阅读来看,似乎使用MULTIEVAL是要走的路。在对聚合对象执行INCRBYEXPIRE操作之后,我需要取回从 INCRBY 操作返回的每个聚合的更新计数。我正在使用 Lettuce 客户端并寻找如何最好地执行此操作的示例。为简单起见,假设聚合对象类似于:

public class Aggregate {
  private String id; // key in Redis
  private Long count; // INCRBY operates on
}
Run Code Online (Sandbox Code Playgroud)

Ima*_*ica 9

好吧,很难给你一个明确的答案,因为结果在很大程度上取决于多种因素:

  1. 网络延迟。数据往返通常比处理本身长得多。如果您需要n命令,这些时间会成倍增加。
  2. 线程数。它们不会帮助您解决延迟问题,但可以使您的连接饱和并为您提供大量吞吐量。
  3. 需要原子性。当你只有打电话incrby,然后expire从作家和get读者,最糟糕的事情可能发生就是什么都没有,我猜。但是如果有其他读者修改但没有过期,那么你可能会错误地过期(writer1> incrby, writer2> set, writer1> expire)。如果您确定您只有第一种类型的编写器,那么您可以节省原子性。

此外,您可能需要针对吞吐量或延迟进行优化。并且专门针对您的环境。正如您稍后将看到的,在不同的设置中会出现不同的方法。

竞争者

原子的

  1. EVAL(增加,过期)。我们发布一个 Lua 脚本来完成这项工作。优点:Redis 保证了 Lua 的原子性。缺点:我们需要一遍又一遍地发送脚本正文。
  2. EVAL(获取,setex)。相同,但我们可以使用不同的命令来完成这项工作。这些将在基准测试的末尾使用 'gs' 调用。
  3. EVALSHA. 我们可以先加载脚本主体,然后只发送它的摘要。优点:命令中的字节更少。缺点:需要确保没有人突然SCRIPT FLUSH把你的脚本带走,而且随着大量的脚本查找时间可能会增加。我预计这会是一个明显的赢家,但脚本足够小,不会成为连接的负担。
  4. MULTI. 我们可以在事务中发出命令。这一次 Redis 会暂存它们,直到您发出EXEC. 这是一个很大的开销,仅用于完整性。

非原子的

  1. 幼稚的。我们可以只发出一个命令,然后再发出另一个。优点:代码简单。
  2. 异步。我们可以利用 luttuce 的异步 API 来跳过等待EXPIRE. 优点:感觉聪明。
  3. 异步刷新。我们可以尝试进一步优化和利用命令流水线。优点:节省带宽,但仅适用于大批量。缺点:您需要一个专用连接并相应地对其进行初始化。

基准

这是代码

你真的应该自己运行它来优化你的需求。我注意到代码中定义的一些选项被忽略,因此请使用命令行参数。

单线程

不过,我会发布我的结果。我用 1 个线程为 3 种不同的连接类型运行它:本地 unix 套接字、本地 TCP、远程 TCP。不幸的是,远程主机的CPU单线程性能要弱得多,所以我必须将其本地结果发布以供参考。不幸的是,这个遥控器上有很多东西在运行,所以它的结果可能会受到影响。

Benchmark                          (address)   Mode  Cnt      Score      Error  Units
RedisBatchesBenchmark.async        socket     thrpt   50  39722.167 ±  796.830  ops/s
RedisBatchesBenchmark.async_flush  socket     thrpt   50  37973.392 ±  239.880  ops/s
RedisBatchesBenchmark.evalsha      socket     thrpt   50  35697.480 ±  216.258  ops/s
RedisBatchesBenchmark.eval         socket     thrpt   50  34616.848 ±  238.412  ops/s
RedisBatchesBenchmark.evalshags    socket     thrpt   50  32642.795 ±  129.305  ops/s
RedisBatchesBenchmark.evalgs       socket     thrpt   50  31679.147 ±  150.264  ops/s
RedisBatchesBenchmark.naive        socket     thrpt   50  20935.985 ±  124.092  ops/s
RedisBatchesBenchmark.multi        socket     thrpt   50  19133.523 ±   72.581  ops/s

RedisBatchesBenchmark.async        localhost  thrpt   50  39179.742 ± 1457.819  ops/s
RedisBatchesBenchmark.async_flush  localhost  thrpt   50  33647.195 ±  295.427  ops/s
RedisBatchesBenchmark.evalsha      localhost  thrpt   50  30679.493 ±  109.669  ops/s
RedisBatchesBenchmark.eval         localhost  thrpt   50  29680.750 ±  161.762  ops/s
RedisBatchesBenchmark.evalshags    localhost  thrpt   50  28069.431 ±  158.887  ops/s
RedisBatchesBenchmark.evalgs       localhost  thrpt   50  27142.783 ±   69.314  ops/s
RedisBatchesBenchmark.naive        localhost  thrpt   50  17934.587 ±  152.859  ops/s
RedisBatchesBenchmark.multi        localhost  thrpt   50  15220.863 ±   32.219  ops/s

RedisBatchesBenchmark.evalsha      remote     thrpt   50   4001.585 ±   12.200  ops/s
RedisBatchesBenchmark.evalgs       remote     thrpt   50   3966.271 ±   16.590  ops/s
RedisBatchesBenchmark.async_flush  remote     thrpt   50   3867.448 ±  153.232  ops/s
RedisBatchesBenchmark.eval         remote     thrpt   50   3622.256 ±  246.454  ops/s
RedisBatchesBenchmark.evalshags    remote     thrpt   50   3607.975 ±  251.802  ops/s
RedisBatchesBenchmark.async        remote     thrpt   50   2681.453 ±   13.451  ops/s
RedisBatchesBenchmark.naive        remote     thrpt   50   2014.806 ±    5.630  ops/s
RedisBatchesBenchmark.multi        remote     thrpt   50   1344.477 ±    3.375  ops/s
------------
Remote host:
Benchmark                          (address)   Mode  Cnt      Score      Error  Units
RedisCommandPacking.naive          socket     thrpt   25  11605,994 ±  368,055  ops/s
RedisCommandPacking.naive          localhost  thrpt   25   9477,190 ±  239,817  ops/s
Run Code Online (Sandbox Code Playgroud)

我们在这里看到什么?

  • 不等待过期结果很快,但是当我们添加延迟(仅 0.194ms ping)时EVALSHA就会提前。请注意即使是最小的延迟也会影响如此快速操作的性能。即使考虑到较慢的 CPU,我们也损失了 5 倍。

  • 两个命令不足以从手动刷新中获得提升(但请注意,我们不会等待过期结果。如果我们等待可能会有所不同)。

  • 这个脚本体足够小,可以与摘要竞争。
  • incrby+expire 比 get+setex 好。
  • 正如预期的那样,Multi 位于底部。
  • async 的错误值非常高,这意味着每次调用的性能差异很大。当您关心最大延迟时,这不是一件好事。

两个线程

Benchmark                       (address)      Mode  Cnt      Score      Error  Units
RedisBatchesBenchmark.async_flush  socket     thrpt   50  97904.168 ± 2497.354  ops/s
RedisBatchesBenchmark.async        socket     thrpt   50  95156.162 ± 1556.060  ops/s
RedisBatchesBenchmark.evalsha      socket     thrpt   50  66418.081 ± 1236.815  ops/s
RedisBatchesBenchmark.eval         socket     thrpt   50  63202.798 ± 1807.753  ops/s
RedisBatchesBenchmark.evalshags    socket     thrpt   50  55259.215 ± 1771.674  ops/s
RedisBatchesBenchmark.evalgs       socket     thrpt   50  54047.418 ± 1143.995  ops/s
RedisBatchesBenchmark.naive        socket     thrpt   50  37771.010 ± 1378.210  ops/s

RedisBatchesBenchmark.async        localhost  thrpt   50  71901.709 ± 1041.603  ops/s
RedisBatchesBenchmark.async_flush  localhost  thrpt   50  66513.162 ± 1353.913  ops/s
RedisBatchesBenchmark.evalsha      localhost  thrpt   50  52052.253 ± 1087.449  ops/s
RedisBatchesBenchmark.eval         localhost  thrpt   50  50806.715 ± 1220.019  ops/s
RedisBatchesBenchmark.evalshags    localhost  thrpt   50  47248.272 ± 1096.685  ops/s
RedisBatchesBenchmark.evalgs       localhost  thrpt   50  44633.113 ± 1829.829  ops/s
RedisBatchesBenchmark.naive        localhost  thrpt   50  35757.586 ± 1455.315  ops/s

RedisBatchesBenchmark.eval         remote     thrpt   50   4030.373 ±   19.535  ops/s
RedisBatchesBenchmark.async        remote     thrpt   50   4010.730 ±   19.527  ops/s
RedisBatchesBenchmark.async_flush  remote     thrpt   50   4004.914 ±   25.449  ops/s
RedisBatchesBenchmark.evalgs       remote     thrpt   50   3983.623 ±   31.027  ops/s
RedisBatchesBenchmark.evalshags    remote     thrpt   50   3978.253 ±   51.875  ops/s
RedisBatchesBenchmark.evalsha      remote     thrpt   50   3949.376 ±   52.273  ops/s
RedisBatchesBenchmark.naive        remote     thrpt   50   2015.643 ±   17.920  ops/s
Run Code Online (Sandbox Code Playgroud)

我们在这里看到什么?

  • 异步得到了很大的提升,尤其是在 unix socket 上。
  • Async 现在非常接近远程的 async_flush。
  • 考虑到复杂性,Async_flush 无利可图。
  • 远程结果彼此非常接近,尤其是考虑到测量误差。
  • 否,MULTI因为它不能用于共享连接,并且应更改基准测试以包含它。这会增加更多的开销,所以不会这样做。