Redis Lua 脚本

RedisRedisBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

在这个实验中,你将探索 Redis Lua 脚本,重点是在 Redis 中直接执行 Lua 脚本,以高效地执行复杂的操作。本实验涵盖使用 EVAL 命令执行脚本、将参数传递给脚本、使用 SCRIPT LOAD 加载脚本以及使用 EVALSHA 运行已加载的脚本。

我们将从执行一个简单的 Lua 脚本开始,该脚本使用 EVAL 递增 Redis 计数器,演示如何指定脚本、它访问的键的数量以及键名。然后,我们将学习如何将脚本加载到 Redis 中,并使用其 SHA1 哈希值执行它们,从而通过避免重复的脚本传输来提高性能。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL redis(("Redis")) -.-> redis/RedisGroup(["Redis"]) redis/RedisGroup -.-> redis/access_cli("Connect Using CLI") redis/RedisGroup -.-> redis/store_string("Set String Value") redis/RedisGroup -.-> redis/fetch_string("Get String Value") redis/RedisGroup -.-> redis/increment_int("Increase Integer Value") subgraph Lab Skills redis/access_cli -.-> lab-552099{{"Redis Lua 脚本"}} redis/store_string -.-> lab-552099{{"Redis Lua 脚本"}} redis/fetch_string -.-> lab-552099{{"Redis Lua 脚本"}} redis/increment_int -.-> lab-552099{{"Redis Lua 脚本"}} end

使用 EVAL 执行 Lua 脚本

在此步骤中,我们将探索如何使用 EVAL 命令直接在 Redis 中执行 Lua 脚本。这允许你在单个请求中对数据执行复杂的操作,从而减少网络延迟并提高性能。

在我们开始之前,让我们了解 EVAL 的基础知识。 EVAL 命令接受两个主要参数:

  1. Lua 脚本本身,作为一个字符串。
  2. 脚本将访问的键的数量,后跟实际的键名和你想要传递给脚本的任何其他参数。

这是一个简单的示例,可帮助你入门。我们将创建一个 Lua 脚本,该脚本递增 Redis 计数器并返回新值。

  1. 打开你的终端并使用 Redis 命令行界面 (redis-cli) 连接到 Redis 服务器。

    redis-cli
  2. 现在,让我们使用 EVAL 执行 Lua 脚本。此脚本将递增名为 mycounter 的计数器,并返回递增后的值。

    EVAL "local current = redis.call('INCR', KEYS[1]); return current" 1 mycounter

    让我们分解一下这个命令:

    • EVAL "local current = redis.call('INCR', KEYS[1]); return current":这是 Lua 脚本本身。它使用 redis.call('INCR', KEYS[1]) 递增 KEYS[1] 中指定的键的值。 KEYS 数组包含传递给脚本的键名。最后,它返回递增后的值。
    • 1:这表示脚本将访问一个键。
    • mycounter:这是脚本将访问的键的名称。

    你应该看到类似于以下的输出:

    (integer) 1

    如果再次运行相同的命令,你将看到计数器递增:

    EVAL "local current = redis.call('INCR', KEYS[1]); return current" 1 mycounter

    输出:

    (integer) 2
  3. 让我们使用 GET 命令验证 mycounter 键的值:

    GET mycounter

    输出:

    "2"

    正如你所看到的,Lua 脚本成功地递增了计数器。

  4. 现在,让我们尝试另一个例子。这次,我们将创建一个脚本,如果某个键尚不存在,则将其设置为特定值。

    EVAL "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], ARGV[1]); return 1 else return 0 end" 1 mykey myvalue

    在此命令中:

    • EVAL "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], ARGV[1]); return 1 else return 0 end":这是 Lua 脚本。它检查键 KEYS[1] 是否存在。如果不存在,它会将键设置为值 ARGV[1] 并返回 1。否则,它返回 0
    • 1:这表示脚本将访问一个键。
    • mykey:这是键的名称。
    • myvalue:如果键不存在,则设置为该值。

    你应该看到类似于以下的输出:

    (integer) 1

    这表示键 mykey 已设置为 myvalue

  5. 让我们检查 mykey 的值:

    GET mykey

    输出:

    "myvalue"

    如果再次运行 EVAL 命令:

    EVAL "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], ARGV[1]); return 1 else return 0 end" 1 mykey myvalue

    输出:

    (integer) 0

    这次,脚本返回 0,因为该键已存在。

  6. 退出 redis-cli。这对于记录更改非常重要。

    exit

使用 EVAL 将参数传递给脚本

在上一步中,我们学习了如何使用 EVAL 执行 Lua 脚本并访问键。现在,让我们探索如何将参数传递给这些脚本。这允许创建更动态和可重用的脚本。

回想一下,EVAL 命令接受以下参数:

  1. Lua 脚本本身,作为一个字符串。
  2. 脚本将访问的键的数量。
  3. 键名。
  4. 你想要传递给脚本的任何其他参数。这些参数在 Lua 脚本中使用 ARGV 数组访问。

让我们从一个例子开始。我们将创建一个 Lua 脚本,该脚本将一个值添加到 Redis 计数器。计数器的键和要添加的值将作为参数传递。

  1. 使用 Redis 命令行界面 (redis-cli) 连接到 Redis 服务器。

    redis-cli
  2. 现在,让我们使用 EVAL 执行一个 Lua 脚本,该脚本将计数器递增指定的量。

    EVAL "local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current" 1 mycounter 5

    让我们分解一下这个命令:

    • EVAL "local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current":这是 Lua 脚本。它使用 redis.call('INCRBY', KEYS[1], ARGV[1])KEYS[1] 中指定的键的值递增 ARGV[1] 中指定的量。
    • 1:这表示脚本将访问一个键。
    • mycounter:这是脚本将访问的键的名称。
    • 5:这是将传递给脚本并作为 ARGV[1] 访问的参数。

    你应该看到类似于以下的输出(假设 mycounter 从上一步开始为 2):

    (integer) 7

    该脚本将 mycounter 键递增了 5。

  3. 让我们使用 GET 命令验证 mycounter 键的值:

    GET mycounter

    输出:

    "7"
  4. 现在,让我们尝试另一个带有字符串参数的示例。我们将创建一个脚本,该脚本将一个键设置为一个值,其中键和值都作为参数传递。

    EVAL "redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]" 1 mynewkey mynewvalue

    在此命令中:

    • EVAL "redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]":这是 Lua 脚本。它将键 KEYS[1] 设置为值 ARGV[1] 并返回值。
    • 1:这表示脚本将访问一个键。
    • mynewkey:这是键的名称。
    • mynewvalue:这是要设置的值。

    你应该看到类似于以下的输出:

    "mynewvalue"
  5. 让我们检查 mynewkey 的值:

    GET mynewkey

    输出:

    "mynewvalue"

    该脚本成功地将键 mynewkey 设置为值 mynewvalue

  6. 你可以将多个参数传递给脚本。例如,让我们创建一个脚本,该脚本连接作为参数传递的两个字符串,并将结果设置为一个键。

    EVAL "local result = ARGV[1] .. ARGV[2]; redis.call('SET', KEYS[1], result); return result" 1 combinedkey hello world

    在此命令中:

    • EVAL "local result = ARGV[1] .. ARGV[2]; redis.call('SET', KEYS[1], result); return result":这是 Lua 脚本。它使用 .. 运算符连接 ARGV[1]ARGV[2],将键 KEYS[1] 设置为结果,并返回结果。
    • 1:这表示脚本将访问一个键。
    • combinedkey:这是键的名称。
    • hello:这是第一个字符串参数。
    • world:这是第二个字符串参数。

    你应该看到类似于以下的输出:

    "helloworld"
  7. 让我们检查 combinedkey 的值:

    GET combinedkey

    输出:

    "helloworld"
  8. 退出 redis-cli。这对于记录更改非常重要。

    exit

使用 SCRIPT LOAD 加载脚本

在之前的步骤中,我们使用 EVAL 命令直接执行 Lua 脚本。虽然这对于简单的脚本很有用,但对于更大、更复杂的脚本来说,它可能会变得很麻烦。Redis 提供了 SCRIPT LOAD 命令,用于将脚本加载到 Redis 服务器的脚本缓存中。这允许你多次执行脚本,而无需每次都发送整个脚本,从而提高性能。

SCRIPT LOAD 命令接受一个参数:Lua 脚本本身。它返回脚本的 SHA1 哈希值,然后你可以使用该哈希值通过 EVALSHA 命令执行脚本(我们将在下一步中介绍)。

让我们看看它是如何工作的。

  1. 使用 Redis 命令行界面 (redis-cli) 连接到 Redis 服务器。

    redis-cli
  2. 现在,让我们使用 SCRIPT LOAD 加载一个 Lua 脚本。我们将使用与上一步中相同的脚本,该脚本将计数器递增指定的量。

    SCRIPT LOAD "local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current"

    此命令会将脚本加载到 Redis 服务器的脚本缓存中,并返回脚本的 SHA1 哈希值。你应该看到类似于以下的输出:

    "6b1e8dd2999cb08546e74339c0c9489f9f89a84b"

    这是脚本的 SHA1 哈希值。记下此哈希值,因为你将在下一步中需要它。确切的哈希值可能会有所不同。

  3. 现在,让我们加载将键设置为值的脚本,其中键和值都作为参数传递。

    SCRIPT LOAD "redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]"

    你应该看到类似于以下的输出:

    "a8b2b3648969459a8198262a9166e945e890987c"

    同样,记下此哈希值。

  4. 让我们加载连接作为参数传递的两个字符串并将结果设置为键的脚本。

    SCRIPT LOAD "local result = ARGV[1] .. ARGV[2]; redis.call('SET', KEYS[1], result); return result"

    你应该看到类似于以下的输出:

    "d2a800a974ca96849295220424f9a0664a495345"

    也记下此哈希值。

  5. 你可以使用 SCRIPT EXISTS 命令验证脚本是否已加载。此命令接受一个或多个 SHA1 哈希值作为参数,并返回一个由 0 和 1 组成的数组,其中 1 表示已加载具有相应哈希值的脚本,而 0 表示未加载。

    例如,要检查我们加载的第一个脚本是否仍然加载,请使用以下命令,将哈希值替换为你从步骤 2 中获得的哈希值:

    SCRIPT EXISTS 6b1e8dd2999cb08546e74339c0c9489f9f89a84b

    输出:

    1) (integer) 1

    这表示已加载该脚本。

    如果你尝试检查未加载的脚本:

    SCRIPT EXISTS 0000000000000000000000000000000000000000

    输出:

    1) (integer) 0

    这表示未加载该脚本。

  6. 退出 redis-cli。这对于记录更改非常重要。

    exit

使用 EVALSHA 运行已加载的脚本

在上一步中,我们学习了如何使用 SCRIPT LOAD 命令将 Lua 脚本加载到 Redis 服务器的脚本缓存中。现在,我们将学习如何使用 EVALSHA 命令执行这些已加载的脚本。

EVALSHA 命令接受以下参数:

  1. 已加载脚本的 SHA1 哈希值。
  2. 脚本将访问的键的数量。
  3. 键名。
  4. 你想要传递给脚本的任何其他参数。

当你需要多次执行同一脚本时,使用 EVALSHAEVAL 更有效,因为它避免了每次都将整个脚本发送到服务器。

让我们看看它是如何工作的。

  1. 使用 Redis 命令行界面 (redis-cli) 连接到 Redis 服务器。

    redis-cli
  2. 现在,让我们使用 EVALSHA 执行将计数器递增指定量的 Lua 脚本。还记得你在上一步中获得的脚本 local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current 的 SHA1 哈希值吗?如果没有,你需要使用 SCRIPT LOAD 再次加载该脚本。在此示例中,我们假设哈希值为 6b1e8dd2999cb08546e74339c0c9489f9f89a84b

    EVALSHA 6b1e8dd2999cb08546e74339c0c9489f9f89a84b 1 mycounter 5

    让我们分解一下这个命令:

    • EVALSHA 6b1e8dd2999cb08546e74339c0c9489f9f89a84b:这指定我们要执行 SHA1 哈希值为 6b1e8dd2999cb08546e74339c0c9489f9f89a84b 的脚本。
    • 1:这表示脚本将访问一个键。
    • mycounter:这是脚本将访问的键的名称。
    • 5:这是将传递给脚本并作为 ARGV[1] 访问的参数。

    你应该看到类似于以下的输出(假设 mycounter 从上一步开始为 7):

    (integer) 12

    该脚本将 mycounter 键递增了 5。

  3. 让我们使用 GET 命令验证 mycounter 键的值:

    GET mycounter

    输出:

    "12"
  4. 现在,让我们使用 EVALSHA 执行将键设置为值的脚本。还记得你在上一步中获得的脚本 redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1] 的 SHA1 哈希值吗?如果没有,你需要使用 SCRIPT LOAD 再次加载该脚本。在此示例中,我们假设哈希值为 a8b2b3648969459a8198262a9166e945e890987c

    EVALSHA a8b2b3648969459a8198262a9166e945e890987c 1 anotherkey anothervalue

    你应该看到类似于以下的输出:

    "anothervalue"
  5. 让我们检查 anotherkey 的值:

    GET anotherkey

    输出:

    "anothervalue"
  6. 如果你尝试使用无效的 SHA1 哈希值执行脚本,你将收到一个错误:

    EVALSHA 0000000000000000000000000000000000000000 1 mykey myvalue

    输出:

    (error) NOSCRIPT No matching script. Please use EVAL.

    这表示未加载具有指定 SHA1 哈希值的脚本。

  7. 退出 redis-cli。这对于记录更改非常重要。

    exit

总结

在这个实验中,你探索了 Redis Lua 脚本,重点是使用 EVAL 命令直接执行脚本。我们了解到 EVAL 接受 Lua 脚本作为字符串、访问的键的数量以及键名本身作为参数。我们练习了在 EVAL 中使用 Lua 脚本递增 Redis 计数器,观察了脚本如何访问和修改指定的键。

此外,我们还学习了如何将参数传递给 Lua 脚本,如何使用 SCRIPT LOAD 将脚本加载到 Redis 中,以及如何使用 EVALSHA 执行已加载的脚本。请记住在每个步骤之后退出 redis-cli,以确保正确记录你的命令。这允许更有效地执行复杂的脚本。