OpenResty interacting with Redis from a script

Posted by zhuizhuhaomeng Blog on January 9, 2024

问题

先向 Redis 服务器获取所有数据,然后在客户端操作过滤需要的数据这种方式显然是低效的。

缺点包过:

  1. 需要通过网络传输大量的数据
  2. 服务端需要编码大量的数据
  3. 客户端还需要解码大量的数据
  4. 客户端要自己执行行过滤逻辑

如果能让服务端执行过滤,那么上面的问题就可以解决。 这就是为什么 Redis 服务器提供了执行 Lua 脚本的功能。 可以参考 Redis 官方的文档

OpenResty 让 Redis server 执行 Lua 脚本的例子

直接执行脚本

这是一个 Lua 脚本,这个脚本是给 OpenResty 执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
local redis = require "resty.redis"
local red = redis:new()

red:set_timeouts(1000, 1000, 1000) -- 1 sec

local ok, err = red:connect("127.0.0.1", 6379)

if not ok then
    ngx.say("failed to connect: ", err)
    return
end

local script = [[
local i =  100
local function set_key()
    local res
    math.randomseed(10000)
    while (i > 0) do
        local val = "res"
        for i =  1, 1000 do
            val = val .. tostring(math.random())
        end 
        res = redis.call('set', KEYS[1], math.random())
        i = i-1
    end
    return res
end

local res = set_key()
return res
]]
for i = 1, 100000000 do
    ok, err = red:eval(script, 1, "10000")
    if not ok then
        ngx.say("failed to set dog: ", err)
        return
    end
end

我们把上述脚本保存为 test.lua, 使用 resty 命令执行。

1
resty test.lua

执行缓存的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
local redis = require "resty.redis"
local red = redis:new()

red:set_timeouts(1000, 1000, 1000) -- 1 sec

local ok, err = red:connect("127.0.0.1", 6379)

if not ok then
    ngx.say("failed to connect: ", err)
    return
end

local script = [[
local i =  100
local function set_key()
    local res
    math.randomseed(10000)
    while (i > 0) do
        local val = "res"
        res = redis.call('set', KEYS[1], math.random())
        i = i-1
    end

    return res
end
-- return set_key()
local res = set_key()
return res
]]

local sha, err = red:script("load", script)
if not sha then
    ngx.say("failed to set dog: ", err)
    return
end

ngx.say(sha)

for i = 1, 100000000 do
    ok, err = red:evalsha(sha, 1, 2)
    if not ok then
        ngx.say("failed to set dog: ", err)
        return
    end
end