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

时间:2018-01-20 18:14:34

标签: java lua redis lettuce

对于Redis中可能已存在或可能不存在的聚合对象的集合,我想对它们调用INCRBY和EXPIRE。从我的阅读来看,似乎使用Porting from Qt WebKit to Qt WebEngineMULTI是可行的方法。在聚合对象上执行EVALINCRBY操作之后,我需要获取从INCRBY操作返回的每个聚合的更新计数。我正在使用Lettuce客户端,并寻找一个如何最好地做到这一点的例子。为简单起见,假设聚合对象类似于:

public class Aggregate {
  private String id; // key in Redis
  private Long count; // INCRBY operates on
}

1 个答案:

答案 0 :(得分:3)

嗯,很难给出明确的答案,因为结果很大程度上取决于多种因素:

  1. 网络延迟。数据往返通常比处理自身长得多。如果您需要n个命令,这些时间会相乘。
  2. 线程数。它们不会帮助您延迟,但可以使您的连接饱和并为您提供大量吞吐量。
  3. 需要原子性。当你只从作者那里打电话给incrby然后再打expire而从读者那里打电话给get时,最糟糕的事情就是......没什么,我猜。但是,如果有其他读者修改但未过期,那么您可能会错误地过期(writer1> incrby,writer2> set,writer1> expire)。如果你确定你只有第一种类型的作者,那么你可以节省原子性。
  4. 此外,您可能还需要针对吞吐量或延迟进行优化。特别适合您的环境。正如您稍后将看到的,在不同的设置中会出现不同的方法。

    竞争者

    Atomical

    1. EVAL(incrby,expire)。我们发布了一个完成这项工作的Lua脚本。优点:Redis保证Lua的原子性。缺点:我们需要一遍又一遍地发送脚本体。
    2. EVAL(get,setex)。同样,但我们可以使用不同的命令来完成这项工作。这些将在基准测试结束时用'gs'调用。
    3. EVALSHA。我们可以先加载脚本体,然后只发送它的摘要。优点:命令中的字节数减少。缺点:需要确保没有人突然SCRIPT FLUSH你的脚本离开,也有很多脚本查找时间可能会增加。我希望这是一个明显的赢家,但脚本足够小,不会成为连接的负担。
    4. MULTI。我们可以在事务中发出命令。这次Redis将会发布它们,直到您发出EXEC。这是一个重要的开销,只是为了完整性而留在这里。
    5. 非atomical

      1. 幼稚。我们可以发出一个命令,然后发出另一个命令。优点:简单的代码。
      2. 异步。我们可以利用luttuce的异步API来跳过等待EXPIRE的结果。优点:感觉很聪明。
      3. Async_flush。我们可以尝试进一步优化和使用command pipelining。优点:节省带宽,但仅适用于大批量生产。缺点:您需要专用连接并相应地进行初始化。
      4. 基准

        这是the code

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

        单线程

        不过,我会发布我的结果。我使用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
        

        我们在这里看到了什么?

        • 不等待过期结果很快,但是当我们增加延迟时(仅仅 0.194ms ping)EVALSHA来了。注意甚至最小延迟会影响这种快速操作的性能。即使考虑到较慢的CPU,我们也失去了5倍。

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

        • 这个脚本体足够小,可以与摘要竞争。
        • incrby + expire比get + setex更好。
        • Multi按预期位于底部。
        • 异步的错误值非常高,这意味着性能与呼叫之间的差异很大。当你关心最大延迟时,这不是一件好事。

        两个线程

        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
        

        我们在这里看到了什么?

        • Async在unix socket上得到了很大的提升。
        • 异步现在非常接近远程的async_flush。
        • Async_flush无利可图,考虑到并发症。
        • 远程结果非常接近,特别是考虑到测量误差。
        • MULTI因为它无法在共享连接上使用,因此应更改基准以包含它。这将增加更多的开销,所以不会这样做。