Lua 脚本是在 Redis 中执行自定义逻辑的强大功能,可以直接在 Redis 服务器上执行。这减少了延迟,提高了性能,并能够实现客户端脚本难以或不可能实现的原子操作。通过在 Redis 中嵌入 Lua 脚本,您可以执行复杂的数据操作和业务逻辑,而无需为每个单独的命令承担网络通信的开销。本课将涵盖在 Redis 中编写和执行 Lua 脚本的基础知识,为您提供有效利用这一功能的知识。
Redis 使用 Lua 作为其脚本语言。Lua 是一种轻量级、可嵌入的脚本语言,以其速度和简洁性而闻名。Redis 在服务器端执行 Lua 脚本,这意味着脚本直接在 Redis 实例内运行。这有几个优势:
Lua 被选为 Redis 脚本语言的原因是:
Redis 中的 Lua 脚本通过一个特殊的 redis
对象与 Redis 数据存储交互。该对象提供了与标准 Redis 命令相对应的函数。
在深入 Redis 特定脚本之前,让我们复习一些基本的 Lua 语法:
变量: 变量无需指定类型声明。
local my_variable = "Hello, Redis!"
local my_number = 123
数据类型: Lua 支持多种数据类型,包括:
string
: 文本数据。number
: 数值数据(整数和浮点数)。boolean
: true
或 false
.table
: 一种通用的数据结构,可以用作数组或字典。nil
: 表示值不存在。注释: 使用 --
进行单行注释。
-- This is a comment
控制流: Lua 提供了标准的控制流语句:
if-then-else
:
local x = 10
if x > 5 then
-- Code to execute if x is greater than 5
else
-- Code to execute otherwise
end
for
循环:
for i = 1, 10 do
-- Code to execute 10 times
end
while
循环:
local i = 1
while i <= 10 do
-- Code to execute while i is less than or equal to 10
i = i + 1
end
功能: 功能使用 function
关键字定义。
function add(a, b)
return a + b
end
local result = add(5, 3) -- result will be 8
redis
对象redis
对象是从 Lua 脚本中与 Redis 交互的主要接口。它提供了一个 call()
函数,允许你执行 Redis 命令。
local value = redis.call('GET', 'mykey')
在这个例子中,redis.call('GET', 'mykey')
对键 mykey
执行 GET
命令并返回值。
这是一个简单的 Lua 脚本,用于增加存储在 Redis 中的计数器:
-- Get the current value of the counter
local current_value = redis.call('GET', KEYS[1])
-- If the counter doesn't exist, initialize it to 0
if not current_value then
current_value = 0
end
-- Increment the counter
local new_value = tonumber(current_value) + 1
-- Set the new value in Redis
redis.call('SET', KEYS[1], new_value)
-- Return the new value
return new_value
在这个脚本中:
KEYS[1]
指的是传递给脚本的第一个键。 Redis 中的 Lua 脚本以数组形式接收键和参数。 KEYS
是一个包含键名称的数组,而 ARGV
是一个包含参数值的数组。redis.call('GET', KEYS[1])
获取计数器的当前值。tonumber(current_value)
将从 Redis 获取的值(始终是字符串)转换为数字。redis.call('SET', KEYS[1], new_value)
设置计数器的新值。考虑一个需要原子方式在两个账户间转账的场景。这可以使用 Lua 脚本实现:
-- KEYS[1]: Source account key
-- KEYS[2]: Destination account key
-- ARGV[1]: Amount to transfer
local source_balance = tonumber(redis.call('GET', KEYS[1]))
local destination_balance = tonumber(redis.call('GET', KEYS[2]))
local amount = tonumber(ARGV[1])
if source_balance and source_balance >= amount then
redis.call('DECRBY', KEYS[1], amount)
redis.call('INCRBY', KEYS[2], amount)
return {1, "OK"} -- Success
else
return {0, "Insufficient funds"} -- Failure
end
在这个脚本中:
KEYS[1]
是源账户余额的密钥。KEYS[2]
是目标账户余额的密钥。ARGV[1]
是转账金额。Lua 脚本在执行过程中可能会遇到错误。优雅地处理这些错误非常重要。如果 Lua 脚本遇到错误,Redis 将自动撤销脚本所做的任何更改,确保原子性。
您可以使用 pcall
(受保护调用)来捕获脚本中的错误:
local status, result = pcall(function()
-- Your code here
return redis.call('GET', 'nonexistent_key')
end)
if status then
-- Code to handle successful execution
if result then
-- Process the result
end
else
-- Code to handle errors
redis.log(redis.LOG_WARNING, "Error: " .. result)
end
在这个例子中:
pcall
在受保护的环境中执行匿名函数。status
将为 true
,result
将包含函数的返回值。status
将是 false
,而 result
将包含错误信息。redis.log
用于将错误信息记录到 Redis 日志中。在 Redis 中执行 Lua 脚本主要有两种方式:
SCRIPT LOAD
命令将脚本加载到 Redis 脚本缓存中。EVAL
命令接受以下参数:
EVAL script numkeys key [key ...] arg [arg ...]
script
: 要执行的 Lua 脚本。numkeys
: 脚本将访问的键的数量。这些键必须立即作为参数传递给 numkeys
。key [key ...]
: 脚本将访问的键。arg [arg ...]
: 脚本可使用的额外参数。示例:
redis-cli EVAL "return redis.call('GET', KEYS[1])" 1 mykey
该命令执行一个 Lua 脚本,获取键 mykey
的值。1
表示该脚本访问一个键,即 mykey
。
EVALSHA
命令接受以下参数:
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
sha1
: Lua 脚本的 SHA1 哈希值。numkeys
: 脚本将访问的键的数量。key [key ...]
: 脚本将访问的键。arg [arg ...]
: 脚本可使用的额外参数。在使用 EVALSHA
之前,你必须使用 SCRIPT LOAD
命令将脚本加载到 Redis 脚本缓存中:
redis-cli SCRIPT LOAD "return redis.call('GET', KEYS[1])"
此命令返回脚本的 SHA1 哈希值。然后您可以使用 EVALSHA
:
redis-cli EVALSHA <sha1_hash> 1 mykey
EVALSHA
仅发送脚本的 SHA1 哈希值,这比脚本本身小得多。这减少了网络带宽,特别是对于大型脚本。让我们重新审视计数器自增的例子,并使用 EVAL
和 EVALSHA
来执行它。
使用 EVAL:
redis-cli EVAL "local current_value = redis.call('GET', KEYS[1]) if not current_value then current_value = 0 end local new_value = tonumber(current_value) + 1 redis.call('SET', KEYS[1], new_value) return new_value" 1 mycounter
使用 EVALSHA:
首先,加载脚本:
redis-cli SCRIPT LOAD "local current_value = redis.call('GET', KEYS[1]) if not current_value then current_value = 0 end local new_value = tonumber(current_value) + 1 redis.call('SET', KEYS[1], new_value) return new_value"
这将返回脚本的 SHA1 哈希值(例如, a7e5b98b9d4a8a2a3b1c2c3d4e5f6a7b8c9d0e1f
)。
然后,使用 EVALSHA
执行脚本:
redis-cli EVALSHA a7e5b98b9d4a8a2a3b1c2c3d4e5f6a7b8c9d0e1f 1 mycounter
Redis 提供了几个用于管理 Lua 脚本的命令:
redis.yield()
函数)。