【问题标题】:Lua script for Redis which sums the values of keysRedis 的 Lua 脚本,用于汇总键的值
【发布时间】:2014-03-09 13:03:35
【问题描述】:

我正在构建我的第一个 Redis 服务器端脚本(用于调试),但缺乏 Lua 经验让我陷入了困境。

基本上有一个 K/V 对的数据集(包含约 1000 个值),我想从中列出与模式匹配的所有 KEY。例如在 redis-cli 中:

> KEYS "carlos:*"
1) "carlos:1"
2) "carlos:2"
3) "carlos:3"
4) "carlos:4"

基于上述输出,我想通过执行 Lua 脚本返回这些键的总和。目前,我的sum.lua 上有以下内容@

local sum = 0
local matches = redis.call('KEYS', 'carlos:*')

for unpack(matches)
   sum = sum + redis.call('GET', matches)
end

return sum

虽然上述脚本可能不正确,但即使尝试 redis.call('KEYS', 'carlos:*') 本身也会产生以下错误

root@carlos:~# redis-cli EVAL "$(cat sum.lua)"

(error) ERR 'eval' 命令的参数数量错误

我已经尝试了多次我的语法迭代,但都无济于事。有什么想法吗?

谢谢

【问题讨论】:

    标签: database scripting compiler-errors lua redis


    【解决方案1】:
    1. EVAL 至少需要两个参数;脚本和您传递给脚本的键数。在这种情况下,您传递零个键,这意味着可以按如下方式调用脚本:

      redis-cli EVAL "$(cat sum.lua)" 0
      

      或:

      redis-cli --eval sum.lua
      
    2. 您用于迭代从KEYS 返回的值的循环结构不正确;我已经为你修好了。

    3. 您需要使用 Lua 的 tonumber 函数将 GET 返回的值从字符串转换为数字。

    进行上述更改后,以下脚本应该适合您:

    local sum = 0
    local matches = redis.call('KEYS', 'carlos:*')
    
    for _,key in ipairs(matches) do
        local val = redis.call('GET', key)
        sum = sum + tonumber(val)
    end
    
    return sum
    

    【讨论】:

    • 请注意,这会将您的脚本永久缓存在 Redis 中,因此您通常应该使用 ARGV 来传递参数,而不是像 carlos:* 这样的硬编码参数。
    【解决方案2】:

    初学者的一些要点:

    KEYS 和 ARGV :这些是 Lua 中的表,它们将在 Redis 中的 Lua 脚本中保存您的 redis 客户端发送的数据。

    • 表是关联数组,是 Lua 的唯一机制 结构化数据。您可以将它们视为数组的等价物 使用您最熟悉的任何语言。表格是 one-based,即索引从 1 开始。所以第一个元素 mytablemytable[1],第二个是mytable[2],以此类推

    • 表不能保存 nil 值。如果一个操作会产生一个表 [ 1, nil, 3, 4 ],结果将改为 [ 1 ] — 表格是 在第一个 nil 值处截断。

    注意:在为 Redis 编写 Lua 脚本时,访问的每个键都应该只能由 KEYS 表访问。 ARGV 表用于参数传递

    我认为您现在可以将键和参数从任何语言传输到您的 Lua 脚本。

    现在我们可以继续 Lua 的redis call 到 redis 命令KEYS

    要遍历返回的数据,您可以选择 lua 的 pairsipairs 并且可以发现一些快速的差异 here。对于上述情况,ipairs 就可以了。

    现在我们可以继续数据类型问题

    Lua 和 Redis 具有不同的类型系统,因此了解跨越 Redis-Lua 边界时值可能如何变化非常重要。当一个数字从 Lua 传回 Redis 客户端时,它变成了一个整数——小数点后的任何数字都将被删除:

    local indiana_pi = 3.2
    return indiana_pi
    

    当你运行这个脚本时,Redis 将返回一个整数 3——你会丢失有趣的 pi 片段。看起来很简单,但是当您在脚本中间开始与 Redis 交互时,事情会变得更加棘手。一个例子:

    local indiana_pi = 3.2
    redis.call("SET", "pi", indiana_pi)
    return redis.call("GET", "pi")
    

    这里的结果值是一个字符串:“3.2” 为什么? Redis 没有专用的数字类型。当我们第一次SET 值时,Redis 将其保存为字符串,丢失了 Lua 最初认为该值是 float 的所有记录。当我们稍后将值拉出来时,它仍然是一个字符串。

    Redis 中使用GET/SET 访问的值应被视为字符串,除非对它们运行像INCRDECR 这样的数字操作。这些特殊的数值运算实际上会返回整数回复(并根据数学规则操作存储的值),但 Redis 中存储的值的“类型”仍然是字符串值。

    以上答案来自这篇很棒的博客文章:

    https://www.redisgreen.net/blog/intro-to-lua-for-redis-programmers/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-27
      • 1970-01-01
      • 2017-06-26
      • 2016-05-20
      • 2018-07-04
      • 2020-11-08
      • 1970-01-01
      • 2014-02-14
      相关资源
      最近更新 更多