【问题标题】:How to call INCRBY and EXPIRE most efficiently in Redis如何在 Redis 中最有效地调用 INCRBY 和 EXPIRE
【发布时间】:2018-06-29 18:48:23
【问题描述】:
对于 Redis 中可能已经存在或不存在的聚合对象的集合,我想对它们调用 INCRBY 和 EXPIRE。根据我的阅读,似乎使用MULTI 或EVAL 是要走的路。在对聚合对象执行 INCRBY 和 EXPIRE 操作之后,我需要取回从 INCRBY 操作返回的每个聚合的更新计数。我正在使用生菜客户端并寻找一个如何最好地做到这一点的例子。为简单起见,假设聚合对象类似于:
public class Aggregate {
private String id; // key in Redis
private Long count; // INCRBY operates on
}
【问题讨论】:
标签:
java
lua
redis
lettuce
【解决方案1】:
嗯,很难给你一个明确的答案,因为结果在很大程度上取决于多种因素:
-
网络延迟。数据往返通常比处理本身要长得多。如果您需要
n 命令,这些时间会成倍增加。
-
线程数。它们不会帮助您解决延迟问题,但可以使您的连接饱和并为您提供大量吞吐量。
-
需要原子性。当你只给作者打电话给
incrby,然后给expire,给读者打电话给get时,最糟糕的事情可能会发生......我猜。但是如果有其他读者修改了,但是没有过期,那么你就可以错误的过期(writer1>incrby, writer2>set, writer1>expire)。如果您确定只有第一种类型的 writer,那么您可以节省原子性。
您可能还需要针对吞吐量或延迟进行优化。并且专门针对您的环境。稍后您将看到,不同的设置会采用不同的方法。
竞争者
原子
-
EVAL(增加,过期)。我们发出一个 Lua 脚本来完成这项工作。优点:Redis 保证了 Lua 的原子性。缺点:我们需要一遍又一遍地发送脚本正文。
-
EVAL(获取,setex)。相同,但我们可以使用不同的命令来完成这项工作。这些将在基准测试的末尾以“gs”调用。
-
EVALSHA。我们可以先加载脚本正文,然后只发送它的摘要。优点:命令中的字节更少。缺点:需要确保没有人突然SCRIPT FLUSH你的脚本离开,还有很多脚本查找时间可能会增加。我预计这将是一个明显的赢家,但脚本足够小,不会成为连接的负担。
-
MULTI。我们可以在事务中发出命令。这一次 Redis 将暂存它们,直到您发出 EXEC。这是一个巨大的开销,只是为了完整性而保留在这里。
非原子
- 天真。我们可以只发出一个命令,然后发出另一个命令。优点:简单的代码。
- 异步。我们可以利用 luttuce 的异步 API 跳过等待
EXPIRE 的结果。优点:感觉很聪明。
- 异步刷新。我们可以尝试进一步优化和利用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
我们在这里看到了什么?
两个线程
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,因为它不能用于共享连接,并且应该更改基准以包含它。这会增加更多开销,所以不要这样做。