redis从2.6.0版本引入对lua的支持

redis服务器以原子方式执行lua脚本,在执行完整个lua脚本前服务器不会执行其他客户端的命令或脚本(即阻塞其他客户端发送的命令执行)

  • 如何使用redis的lua脚本特性
  • 如何管理脚本
  • 如何使用lua环境内置的函数库
  • 如何调试脚本

14.1 EVAL:执行脚本

1
eval script numKeys key [key ...] arg [arg ...]

其中:

  • script 指的是lua脚本
  • numKeys 指定了有多少个key,后面紧跟这么多个key。在lua中,可以通过KEYS数组进行访问,1-index
  • 任意个arg,lua脚本中通过ARGV数组进行访问,1-index

14.1.1 在lua脚本中使用redis服务器提供的命令

  • redis.call()
  • redis.pcall() 执行遇到错误时会返回一个包含错误信息的lua表格

14.1.2 值转换

  • 当脚本调用redis.call()的时候,传入的lua值将会被转换成redis协议的值
  • redis.call()返回值将会从redis值被装换为lua值
  • lua脚本执行结束时,将会把lua值转换为redis协议值

将redis协议值转换成lua值的规则 将redis协议值转换成lua值的规则

将lua值转换为redis协议值的规则 将lua值转换为redis协议值的规则

注意:

  • 因为浮点数的lua值被转换为redis值时会被强转为整型,需要有意识的用lua的tostring()转换后再返回给redis

在脚本中切换数据库

14.2 SCRIPT LOAD 和 EVALSHA: 缓存、执行脚本

SCRIPT LOAD 将lua脚本加载到redis服务器中,并返回标识脚本的sha1值:

1
SCRIPT LOAD script 

EVALSHA 根据提供的sha1值调用对应的lua脚本 :

1
EVALSHA sha1 numKeys key [key ...] arg [arg ...]

14.3 脚本管理

  • SCRIPT EXISTS sha1 [sha1 ...] 检查脚本是否存在
    • 存在返回1,否则返回0
  • SCRIPT FLUSH 移除redis服务器所有缓存的脚本
  • SCRIPT KILL 强制停止正在运行的脚本
    • 配置选项lua-time-limit定义了lua脚本可以不受限制运行的时长,默认5000 ms
    • 当超过 lua-time-limit 时长时,想服务器发送请求的客户端将得到一个错误回复,提示用户使用SCRIPT KILLSHUTDOWN NOSAVE
    • 注意,如果超过 lua-time-limit 后,如果在已经执行过写操作的情况下,强行SCRIPT KILL可能会产生脏数据,事实上若执行SCRIPT KILL会报错提示UNKILLABLE。此时只能等待脚本执行完毕,或者执行 SHUTDOWN NOSAVE 在不执行持久化操作的情况下关闭服务器

14.4 redis给lua提供的内置函数库

redis提供了以下函数库、函数包,供编写lua脚本时使用:

  • base、table、string、math (lua标准库)
  • redis (调用redis功能专用的定制包)
  • bit、struct、cjson、cmsgpack (从外部引入的数据处理包)

14.4.1 redis包

  • reids.log()
    • 用redis服务器打日志
    • 提供四个级别:redis.LOG_DEBUG, redis.LOG_VERBOSE, redis.LOG_NOTICE, redis.LOG_WARNING
  • redis.sha1hex()
    • 计算给定字符串的SHA1校验和
  • redis.error_reply()
    • 返回redis错误回复
    • 返回一个只包含err字段的lua table, 其中,err字段的值则是给定的错误信息
    • 例如:redis.error_reply('something wrong') 将返回table {err='something wrong'}
  • redis.status_reply()
    • 返回redis状态回复
    • 例如,redis.status_reply('all is well') 将返回table {ok='all is well'}
  • redis.breakpoint()
  • redis.debug()
  • redis.replicate_commands()
  • redis.set_repl()

14.4.2 bit包

bit包可以对lua脚本中的数字进行二进制位操作:

  • bit.tohex()
  • bit.bnot()
  • bit.bor()
  • bit.band()
  • bit.bxor()
  • ….

14.4.3 struct 包

struct 包提供了能够在lua值以及c结构体之间进行转换的基本设施

  • struct.pack(fmt, v1, v2, ...)
  • struct.unpack(fmt, s, [i])
  • struct.size(fmt)

14.4.4 cjson包

提供json编码和解码操作 :

  • cjson.encode(value)
  • cjson.decode(json_text)

14.4.5 cmsgpack包

为lua脚本提供快速的MessagePack打包和解包操作

  • cmsgpack.pack(arg1, arg2, ...)
  • cmsgpack.unpack(msgpack)

14.5 脚本调试

从redis3.2 开始引入一个lua调试器,简称为LDB。

用户可以通过LDB实现单步调试、添加断点、返回日志、打印调用链、重载脚本等多种功能

通过使用redis-cli指定参数的方式使用:

1
redis-cli --ldb --eval xxx.lua 键名, 参数

调试命令: 调试命令列表

  • 动态断点
  • 使用redis.debug()输出调试日志
  • 执行指定的代码或指令
  • 使用trace显示调用链
  • 可以使用restart重新载入(修改后的)脚本
  • 调试模式
    • redis调试器支持两种不同的调试模式
    • 1)异步调试
      • 以参数ldb启动
      • 为每个调试会话分别创建新的子进程
      • 使得允许多个用户同时进行调试
      • 在子进程上进行,不会阻塞服务器,也不会影响服务器数据
    • 2)同步调试:
      • 以参数ldb-sync-mode启动
      • 直接使用服务器进程进行调试,所以同一时间只能有一个调试会话
      • 会影响数据