跳至主要內容

Redis中的Lua脚本

holic-x...大约 4 分钟碎片化碎片化

Redis中的Lua脚本

参考学习资料

  • todo

Lua 脚本

1.Redis 中的Lua 脚本

Redis 的 Lua 脚本功能允许用户在 Redis 服务器端执行自定义的 Lua 脚本,以实现原子操作和复杂逻辑

  • 其核心点包括:

    • 原子性:Lua 脚本的所有命令在执行过程中是原子的,避免了并发修改带来的问题

    • 减少网络往返次数:通过在服务器端执行脚本,减少了客户端和服务器之间的网络往返次数,提高了性能

    • 复杂操作:可以在 Lua 脚本中执行复杂的逻辑,比如批量更新、条件更新等,超过了单个 Redis 命令的能力

​ 例如常见基于 Redis 实现分布式锁就需要结合 lua 脚本来实现

lua 本身是不具备原子性的,但由于 Redis 的命令是单线程执行的,它会把整个lua 脚本作为一个命令执行,会阻塞其间接受到的其他命令,这就保证了lua 脚本的原子性

2.Lua脚本使用

核心概念

​ Lua 语言是一门轻量级的脚本语言,使用C语言编写,具有简洁的语法,易于学习和使用,广泛应用于游戏开发嵌入式系统等领域。它设计的主要目的就是为了嵌入其他程序,实现灵活的扩展和定制功能,并且具备快速的执行速度,能够满足各种开发需求

Lua 语言具有以下优点:

1.轻量级:占用资源少,易于嵌入其他程序

2.简洁高效:语法简单,执行效率高

3.跨平台:可在多种操作系统上运行

4.扩展性强:方便与其他语言集成

5.快速开发:减少开发时间和成本

6.灵活性高:适应各种不同的项目需求

7.易于学习:入门难度低,容易掌握

8.解释执行:无需编译,便于调试

9.资源占用少:对系统资源的需求相对较低

10.社区活跃:有丰富的文档和工具支持

基本语法

Lua 脚本基本规则

  • 调用Redis命令
    • 使用redis.call调用Redis原生命令
    • 使用redis.pcall进行安全调用(如果脚本中某些命令执行失败可以捕获错误,而不是中断执行,可以支持在脚本内处理错误)
  • 参数传递
    • KEYS表示传入的键名数组
    • ARGV表示传入的参数数组

语法规则

# 语法规则
EVAL script numkeys key [key ...] arg [arg ...]
  • script 是要执行的Lua脚本
  • numkeys 是传递给脚本的键数量
  • key [key ...] 是被Lua脚本使用的键
  • arg [arg ...] 是传递给Lua脚本的参数

应用场景

原子计数器

​ 使用INCR原子性递增counter_key的值:

  • 调用redis.call('INCR', KEYS[1])实现递增
  • 返回递增后的值
EVAL "local current = redis.call('INCR', KEYS[1]) return current" 1 counter_key

限流控制

​ 简单限流器的实现,每分钟允许访问100次

  • 检查当前访问次数是否超出限制
  • 如果未超出限制,则增加计数并设置过期时间
EVAL [[
  local current = tonumber(redis.call('GET', KEYS[1]) or 0)
  if current < ARGV[1] then
    redis.call('INCR', KEYS[1])
    redis.call('EXPIRE', KEYS[1], 60)
    return true
  else
    return false
  end
]] 1 rate_limit_key 100

预加载脚本

​ 可以使用 SCRIPT LOAD 将 Lua 脚本预加载到 Redis,以提高执行效率

SCRIPT LOAD "return redis.call('GET', KEYS[1])"

应用注意点

(1)lua 脚本执行

​ lua 本身不具备原子性,是因为Redis是单线程执行指令,因此将多个命令打包到lua脚本中,再由redis执行进而确保操作的原子性。

​ 因此 Redis 执行 lua 脚本期间是无法处理其他命令,因此如果 lua 脚本的业务过于复杂,则会产生长时间的阻塞,所以编写 Lua 脚本时应尽量保持简短和高效。Redis 默认限制 lua 执行脚本时间为 5s,如果超过这个时间则会终止且抛错,可以通过 lua-time-limit 调整时长

(2)如果lua脚本中的部分命令执行失败会怎样?
-- 正确的 SET 命令
redis.call("SET", "my_key", "mianshiya")

-- 错误的 HSET 命令
redis.call("HSET", "my_hash", "field_mianshiya") -- 缺少值参数

​ 如果执行上述lua脚本,则会发现my_key会被正常执行,但由于hset缺少值参数导致命令执行报错,但my_key是不会回滚的。因此实际上lua脚本并不能完全保证原子性,但官方认为这种错误正常情况下不可能发生(属于开发者的人为因素:例如语法错误、执行了不该执行的命令 导致的问题)

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3