Redis 的 Lua 脚本功能是什么?如何使用?
Redis 的 Lua 脚本功能是什么?如何使用?
回答重点
Redis 的 Lua 脚本功能允许用户在 Redis 服务器端执行自定义的 Lua 脚本,以实现原子操作和复杂逻辑。
其核心点包括:
- 原子性:Lua 脚本的所有命令在执行过程中是原子的,避免了并发修改带来的问题。
- 减少网络往返次数:通过在服务器端执行脚本,减少了客户端和服务器之间的网络往返次数,提高了性能。
- 复杂操作:可以在 Lua 脚本中执行复杂的逻辑,比如批量更新、条件更新等,超过了单个 Redis 命令的能力。
例如常见基于 Redis 实现分布式锁就需要结合 lua 脚本来实现。
lua 本身是不具备原子性的,但由于 Redis 的命令是单线程执行的,它会把整个 lua 脚本作为一个命令执行,会阻塞其间接受到的其他命令,这就保证了 lua 脚本的原子性。
扩展知识
Lua 脚本的基本结构
调用 Redis 命令:
- 使用
redis.call调用 Redis 原生命令。 - 使用
redis.pcall进行安全调用,如果脚本中的某些命令失败,可以捕获错误,而不是直接中断执行。这样可以在脚本内处理错误。
参数传递:
KEYS表示传入的键名数组。ARGV表示传入的参数数组。
Lua 脚本使用示例
基本的 GET 和 SET 操作
1 | EVAL |
分析:
- 这段脚本首先获取键
mykey的值。 - 如果
mykey不存在,则设置其值为default_value。 - 返回
mykey的当前值。
步骤:
redis.call('GET', KEYS[1])获取键的值。if not value then redis.call('SET', KEYS[1], ARGV[1]) end检查值是否存在,不存在则设置默认值。return value返回当前值。
原子递增计数器
1 | EVAL "local current = redis.call('INCR', KEYS[1]) return current" 1 counter_key |
分析:
- 这段脚本使用
INCR原子性地递增键counter_key的值。
步骤:
redis.call('INCR', KEYS[1])直接递增。- 返回递增后的值。
限流控制
假设我们要实现一个简单的限流器,每分钟允许访问 100 次。
1 | EVAL [[ |
分析:
- 该脚本检查当前的访问次数是否超过限制。
- 如果未超过限制,则增加计数并设置过期时间。
步骤:
local current = tonumber(redis.call('GET', KEYS[1]) or 0)获取当前计数,默认为 0。- 判断是否小于限制值
ARGV[1]。 - 若未超过,则
INCR当前计数并设置过期时间。 - 返回布尔值表示是否允许访问。
Lua 预加载脚本
可以使用 SCRIPT LOAD 将 Lua 脚本预加载到 Redis,以提高执行效率:
1 | SCRIPT LOAD "return redis.call('GET', KEYS[1])" |
此命令返回脚本的 SHA1 哈希值,后续可用 EVALSHA 命令通过该哈希值执行。
Lua 脚本使用注意点
由于 Redis 执行 lua 脚本其间,无法处理其他命令,因此如果 lua 脚本的业务过于复杂,则会产生长时间的阻塞,因此编写 Lua 脚本时应尽量保持简短和高效。
Redis 默认限制 lua 执行脚本时间为 5s,如果超过这个时间则会终止且抛错,可以通过 lua-time-limit 调整时长。
如果 Lua 脚本中的部分命令执行失败会怎样?
例如以下这段 lua 脚本:
1 | -- 正确的 SET 命令 |
此时,执行上述的 lua 脚本, my_key 会被成功执行,由于 HSET 缺少参数,会报错,而 my_key 并不会被回滚。
所以实际上 lua 脚本并不能完全保证原子性,但是官方认为这种错误正常情况下不可能发生,属于开发者的人为因素(语法错误、或者执行了不应该执行的命令)导致的,他们不背这个锅。
Lua 知识点
Lua 语言是一门轻量级的脚本语言,使用 C 语言编写,具有简洁的语法,易于学习和使用,广泛应用于游戏开发、嵌入式系统等领域。
它设计的主要目的就是为了嵌入其他程序,实现灵活的扩展和定制功能,并且具备快速的执行速度,能够满足各种开发需求。
Lua 语言具有以下优点:
- 轻量级:占用资源少,易于嵌入其他程序。
- 简洁高效:语法简单,执行效率高。
- 跨平台:可在多种操作系统上运行。
- 扩展性强:方便与其他语言集成。
- 快速开发:减少开发时间和成本。
- 灵活性高:适应各种不同的项目需求。
- 易于学习:入门难度低,容易掌握。
- 解释执行:无需编译,便于调试。
- 资源占用少:对系统资源的需求相对较低。
- 社区活跃:有丰富的文档和工具支持。