redis - 与在 redis-cli 客户端上运行的本机命令相比,为什么 EVALSHA 命令会带来如此高的性能成本?
问题描述
以下是我针对 redis-benchmark 工具运行的一些测试和结果。
C02YLCE2LVCF:Downloads xxxxxx$ redis-benchmark -p 7000 -q -r 1000000 -n 2000000 JSON.SET fooz . [9999]
JSON.SET fooz . [9999]: 93049.23 requests per second
C02YLCE2LVCF:Downloads xxxxxx$ redis-benchmark -p 7000 -q -r 1000000 -n 2000000 evalsha 8d2d42f1e3a5ce869b50a2b65a8bfaafe8eff57a 1 fooz [5555]
evalsha 8d2d42f1e3a5ce869b50a2b65a8bfaafe8eff57a 1 fooz [5555]: 61132.17 requests per second
C02YLCE2LVCF:Downloads xxxxxx$ redis-benchmark -p 7000 -q -r 1000000 -n 2000000 eval "return redis.call('JSON.SET', KEYS[1], '.', ARGV[1])" 1 fooz [5555]
eval return redis.call('JSON.SET', KEYS[1], '.', ARGV[1]) 1 fooz [5555]: 57423.41 requests per second
对于运行脚本的服务器端与运行脚本客户端的客户端相比,本应具有性能优势的东西的性能显着下降。
从客户端到EVALSHA
= 34% 的性能损失
从EVALSHA
到EVAL
= 6% 的性能损失
非 JSON 插入set
命令的结果相似
C02YLCE2LVCF:Downloads xxxxxx$ redis-benchmark -p 7000 -q -r 1000000 -n 2000000 set fooz 3333
set fooz 3333: 116414.43 requests per second
C02YLCE2LVCF:Downloads xxxxxxx$ redis-benchmark -p 7000 -q -r 1000000 -n 2000000 evalsha e32aba8d03c97f4418a8593ed4166640651e18da 1 fooz [2222]
evalsha e32aba8d03c97f4418a8593ed4166640651e18da 1 fooz [2222]: 78520.67 requests per second
当我执行 info commandstat 并观察到该EVALSHA
命令的性能较差时,我首先注意到了这一点
# Commandstats
cmdstat_ping:calls=331,usec=189,usec_per_call=0.57
cmdstat_eval:calls=65,usec=4868,usec_per_call=74.89
cmdstat_del:calls=2,usec=21,usec_per_call=10.50
cmdstat_ttl:calls=78,usec=131,usec_per_call=1.68
cmdstat_psync:calls=51,usec=2515,usec_per_call=49.31
cmdstat_command:calls=5,usec=3976,usec_per_call=795.20
cmdstat_scan:calls=172,usec=1280,usec_per_call=7.44
cmdstat_replconf:calls=185947,usec=217446,usec_per_call=1.17
****cmdstat_json.set:calls=1056,usec=26635,usec_per_call=25.22**
****cmdstat_evalsha:calls=1966,usec=68867,usec_per_call=35.03**
cmdstat_expire:calls=1073,usec=1118,usec_per_call=1.04
cmdstat_flushall:calls=9,usec=694,usec_per_call=77.11
cmdstat_monitor:calls=1,usec=1,usec_per_call=1.00
cmdstat_get:calls=17,usec=21,usec_per_call=1.24
cmdstat_cluster:calls=102761,usec=23379827,usec_per_call=227.52
cmdstat_client:calls=100551,usec=122382,usec_per_call=1.22
cmdstat_json.del:calls=247,usec=2487,usec_per_call=10.07
cmdstat_script:calls=207,usec=10834,usec_per_call=52.34
cmdstat_info:calls=4532,usec=229808,usec_per_call=50.71
cmdstat_json.get:calls=1615,usec=11923,usec_per_call=7.38
cmdstat_type:calls=78,usec=115,usec_per_call=1.47
从JSON.SET
到EVALSHA
有大约 30% 的性能降低,这是我在直接测试中观察到的。
问题是,为什么?而且,这是否值得关注,或者这种观察是否在合理的预期范围内?
对于上下文,我使用EVALSHA
而不是直接 JSON.SET 命令的原因有两个。
IORedis 客户端库不直接支持使用 RedisJson。
由于前面的事实,我将不得不使用 send_command() ,然后它将直接命令发送到服务器,但在使用 TypeScript 时不能使用流水线。所以我不得不单独执行所有其他命令并放弃流水线。
我认为这应该是更好的表现?
****** 更新:
所以最后,根据下面的答案,我重构了我的代码,只包含 1EVALSHA
用于写入,因为它使用了 2 个命令,即 set 和 expire 命令。同样,我不能将它单独放入 RedisJson,这就是原因。
这是供某人参考的代码:Shows evalsha and fallback
await this.client.evalsha(this.luaWriteCommand, '1', documentChange.id, JSON.stringify(documentChange), expirationSeconds)
.catch((error) => {
console.error(error);
evalSHAFail = true;
});
if (evalSHAFail) {
console.error('EVALSHA for write not processed, using EVAL');
await this.client.eval("return redis.pcall('JSON.SET', KEYS[1], '.', ARGV[1]), redis.pcall('expire', KEYS[1], ARGV[2]);", '1', documentChange.id, JSON.stringify(documentChange), expirationSeconds);
console.log('SRANS FRUNDER');
this.luaWriteCommand = undefined;
解决方案
为什么 Lua 脚本在您的情况下较慢?
因为EVALSHA
需要比单个JSON.SET
或SET
命令做更多的工作。运行时EVALSHA
,Redis 需要将参数推送到 Lua 堆栈,运行 Lua 脚本,并从 Lua 堆栈中弹出返回值。它应该比对JSON.SET
or的 ac 函数调用慢SET
。
那么服务器端脚本什么时候有性能优势呢?
首先,您必须在脚本中运行多个命令,否则不会有我上面提到的任何性能优势。
其次,服务器端脚本运行速度比一个接一个地向 Redis 发送 serval 命令更快,从 Redis 中获取结果,并在客户端进行计算工作。因为,Lua 脚本节省了大量往返时间。
第三,如果您需要在 Lua 脚本中进行非常复杂的计算工作。这可能不是一个好主意。因为Redis在单线程中运行脚本,如果脚本耗时过长,会阻塞其他客户端。相反,在客户端,您可以利用多核来进行复杂的计算。
推荐阅读
- swift - 防止其他人发送虚假 HTTP 发布请求正文的最佳方法是什么?
- javascript - 如何在没有用户点击的情况下复制
- arrays - Fixed form Fortran Allocate array with size to be read from file
- c# - 在 C# 中堆叠盒子而不超过特定高度
- laravel - Laravel:如果没有其他模型共享它,则级联删除模型
- c - 过滤c中的垃圾值
- excel - 替换250个数据透视表的sourcedata中的字母
- android - android.view.InflateException 膨胀类错误
- linux - IOHIDManager.h:没有这样的文件或目录
- reactjs - 有没有办法访问谷歌照片所做的照片分类?或者最近浏览过的照片?