【问题标题】:How to call INCRBY and EXPIRE most efficiently in Redis如何在 Redis 中最有效地调用 INCRBY 和 EXPIRE
【发布时间】:2018-06-29 18:48:23
【问题描述】:

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

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

【问题讨论】:

    标签: java lua redis lettuce


    【解决方案1】:

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

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

    您可能还需要针对吞吐量或延迟进行优化。并且专门针对您的环境。稍后您将看到,不同的设置会采用不同的方法。

    竞争者

    原子

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

    非原子

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

    基准

    这里是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 与预期一样位于底部。
    • 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
    

    我们在这里看到了什么?

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多