Redis是一种基于内存的用于进程间通信和数据存储的软件工具。您可能听说过它可以运行Lua脚本,但是您仍然不确定怎么使用。那么请继续阅读本文。
1、前置条件
您应该在系统上安装Redis才能运行本文中的例子。阅读本文时对照Redis命令参考可能会更有帮助。
2、为什么需要Lua脚本?
简而言之:性能提升。您在Redis中执行的大多数任务都涉及许多步骤。您可以使用Lua在Redis内部进行操作,而不必使用应用程序语言来执行这些步骤。
- 这可能会导致更好的性能。
- 同样,脚本中的所有步骤都以原子方式执行。执行脚本时,无法运行其他Redis命令。
例如,我使用Lua脚本改变存储在Redis的JSON字符串。我将在本文后半部分对此进行详细描述。
3、可是我什么都不知道
一个不认识Lua的人
别担心,Lua并不是很难理解。如果您了解C语言系列中的任何语言,那么您应该很容易上手Lua。另外,我在本文中提供了代码示例。
4、给我看个例子
让我们开始通过redis-cli运行脚本。从以下内容开始:
redis-cli
现在运行以下命令:
eval “redis.call(‘set’, KEYS[1], ARGV[1])” 1 key:name value
该EVAL命令就是告诉Redis的运行下面的脚本。该”redis.call(‘set’, KEYS[1], ARGV[1])” 字符串是我们的脚本,其功能与Redis的set命令相同。脚本文本后面跟随三个参数:
- 键的个数
- 键名
- 键值
脚本参数分为两类:KEYS和ARGV。
我们用紧随其后的数字指定脚本需要多少个键。在我们的示例中,该值为1。在此编号之后,我们需要立即接这些key。它们可以作为脚本中的KEYS表访问。在我们的例子中,它key:name在索引1处包含一个值。
注意,Lua索引表从索引 1开始,而不是0。
我们可以在键之后提供任意数量的参数,这些参数可以在Lua中作为ARGV表使用。在此示例中,我们提供了一个ARGV参数:string value。您已经猜到了,上面的命令将键设置key:name为value value。
提供脚本使用的key作为KEYS以及提供所有其他参数作为ARGV是一种好习惯。因此,您不应该将KEYS指定为0,然后在ARGV表中提供所有key。
现在让我们检查脚本是否成功完成。我们将通过运行另一个从Redis获取密钥的脚本来做到这一点:
eval “return redis.call(‘get’, KEYS[1])” 1 key:name
输出应该为”value”,这意味着先前的脚本成功设置了键值 “key:name”`。
5、你能解释一下脚本吗?
我们的第一个脚本包含一个语句:redis.call函数:
redis.call('set',KEYS [1],ARGV [1])
使用redis.call它可以执行任何Redis命令。第一个参数是此命令的名称,后跟其参数。对于set命令,这些参数是key和value。支持所有Redis命令。根据文档:
Redis使用相同的Lua解释器来运行所有命令
我们的第二个脚本不仅仅运行一个命令,它还返回一个值:
eval “return redis.call(‘get’, KEYS[1])” 1 key:name
脚本返回的所有内容都发送到调用过程。在我们的情况下,此过程为redis-cli,您将在终端窗口中看到结果。
6、还有更复杂的东西吗?
我曾经使用Lua脚本以特定顺序从哈希映射中返回元素。顺序是存储在有序集order键中。
首先,通过在redis-cli中运行以下命令来设置数据:
hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6zadd order 1 key:3 2 key:1 3 key:2
这条命令处创建了一个名为:hkeys的哈希映射,filed名为:key:XXX ,order其中包含hkeys按特定顺序从中选择filed。
您可能需要查看hmset和zadd命令参考以获取详细信息。
让我们运行以下脚本:
eval “local order = redis.call(‘zrange’, KEYS[1], 0, -1); return redis.call(‘hmget’,KEYS[2],unpack(order));” 2 order hkeys
您应该看到以下输出:
“value:3”“value:1”“value:2”
这意味着我们以正确的顺序获得了所需hash filed的值。
7、是否必须指定完整的脚本文本才能运行它?
Redis允许您使用SCRIPT LOAD命令将脚本预加载到内存中:
script load “return redis.call(‘get’, KEYS[1])”
您应该看到如下输出:
“4e6d8fc8bb01276962cce5371fa795a7763657ae”
这是您需要提供给EVALSHA命令以运行脚本的脚本的唯一哈希:
evalsha 4e6d8fc8bb01276962cce5371fa795a7763657ae 1 key:name
注意:您应该使用SCRIPT LOAD命令返回的实际SHA1哈希,上面的哈希只是一个示例。
8、更改JSON的内容
有时人们在Redis中存储JSON对象。可以查看我前面一篇文章的讲解《Redis使用字符串和hash存储JSON,哪个更高效?》。
如果必须在此JSON对象中更改key,则需要从Redis中获取key的value值,然后对其进行解析,更改key的value值,然后进行序列化并将其设置回Redis。这种方法存在两个问题:
- 并发。另一个过程可以在我们的get和set操作之间更改此JSON。在这种情况下,更改将丢失。
- 性能。如果您经常进行这些更改,并且json对象很大,则可能成为应用程序的瓶颈。您可以通过在Lua中实现此逻辑来提高一些性能。
让我们在key下的Redis中添加一个测试JSON字符串obj:
set obj ‘{“a”:”foo”,”b”:”bar”}’
现在运行脚本:
EVAL ‘local obj = redis.call(“get”,KEYS[1]); local obj2 = string.gsub(obj,”(“ .. ARGV[1] .. “”:)([^,}]+)”, “%1” .. ARGV[2]); return redis.call(“set”,KEYS[1],obj2);’ 1 obj b bar2
现在我们将在key下具有以下对象obj:
{“ a”:“ foo”,“ b”:“ bar2”}
您可以使用SCRIPT LOAD命令来加载此脚本,然后像这样运行它:
EVALSHA 1 obj b bar2
一些注意事项:
- 1、脚本中..是Lua中字符串连接运算符。
- 2、我们使用RegEx模式来匹配键并替换其值。
- 3、Lua RegEx风味与大多数其他风格的不同之处在于,我们%同时将它们用作RegEx特殊符号的回溯标记和转义字符。
9、我应该一直使用Lua脚本吗?
不。我建议仅在可以证明它可以带来更好的性能时才使用它们。首先都要做基准测试,确定性能会提高才需要使用lua脚本。如果您只需要原子性,则应改为检查Redis事务。
另外,您的脚本不应太长。请记住,脚本运行时,其他所有内容都在等待脚本完成。如果您的脚本需要花费一些时间,则可能会导致瓶颈,而不是提高性能。脚本在达到超时(默认为5秒)后停止。有关Lua的更多信息,请访问lua.org。
感谢您阅读本文。欢迎评论和转发。更多相关编程信息请关注我