Redis列表上的Lua数值函数

我刚开始接触Lua和Redis。我考虑使用Redis来支持一些实时查询。作为概念证明和性能检验,我对列表中的值求和。

docker-compose.yml

version: '3.1'

services:
  redis:
    image: redis:4.0.13-alpine
    ports:
      - "6379:6379"

python脚本

import redis
import numpy as np

n = 1000000

redis_host = "localhost"
redis_port = 6379
redis_password = ""

arr = np.random.rand(n)

r = redis.StrictRedis(host=redis_host, port=redis_port, password=redis_password, decode_responses=True)
r.delete('mylist')
r.lpush('mylist', *arr.tolist())
print(np.sum(arr))

lua_sum = """
local result = redis.call('lrange', KEYS[1], 0, -1)
local sum = 0
for i=1, #result, 1 do
    sum = sum + result[i]
end
return tostring(sum)
"""

f = r.register_script(lua_sum)
print(f(keys=['mylist']))

使用ipython timeit,Redis版本需要一点多一秒的时间。我可能(幼稚地?)预计至少要比numpy快一个数量级。脚本中是否有任何极其低效的地方?请注意,我意识到这种特定用例可以通过预处理来处理,但这只是一个简单的开始。

编辑 特别是timeit命令:

In [2]: %timeit f(keys=['mylist'])
1 loop, best of 3: 1.31 s per loop

In [3]: %timeit np.sum(arr)
1000 loops, best of 3: 1.04 ms per loop
点赞
用户5384363
用户5384363

你的 Lua 脚本调用 LRANGE 从 Redis 中获取 100 万个数字,即 LRANGE key, 0, -1,这非常低效。Redis 需要从列表中读取 100 万个数字,并将这些数据传递给 Lua。这将需要很长时间。

另外,你试图在一次调用中将 100 万个数字推送到 Redis,这也是一个非常糟糕的想法,因为它可能会导致 Redis 长时间阻塞。

2019-02-23 16:08:56
用户172363
用户172363

我认为您真正需要的是类似于redis-benchmark的东西。它已经包含在redis的分布中。

从命令行redis-cli中,我使用类似于您的列表向Redis的空实例添加了如下命令:

127.0.0.1:6379>eval "local result = redis.call('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, 1 do sum = sum + result[i]; end; return tostring(sum);" 0

然后,我运行了redis-benchmark

$ redis-benchmark -n 100000 script load "local result = redis.call ('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, 1 do sum = sum + result[i]; end; return tostring(sum);"
script load local result = redis.call ('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, script load local result = redis.call ('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, script load local result = redis.call ('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, script load local result = redis.call ('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, script load local result = redis.call ('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, ====== script load local result = redis.call('lrange', 'mylist', 0, -1); local sum = 0; for i=1, #result, 1 do sum = sum + result[i]; end; return tostring(sum); ======
  100000个请求在1.20秒内完成
  50个并发客户端
  3字节有效载荷
  保持连接: 1

99.99%<=1毫秒
100.00%<=1毫秒
83263.95请求每秒

正如您所看到的,结果与您显示的结果非常不同。100,000次脚本调用完成了1.2秒。

我不知道为什么您的测试表现非常糟糕,但我可以想到几种可能性:

1.您的docker镜像中的内存/处理器限制 2.网络延迟 3.您正在使用的Python-redis库的开销

但基本上,您不应该将Redis用于处理绑定脚本。由于Redis是单线程的,lua脚本将阻塞所有其他脚本的执行,直到它完成。

我建议您使用redis-benchmark进行进一步的了解Redis的性能。

2019-03-16 06:35:49