目录

第一个lua脚本

Lua 脚本获取 EVAL & EVALSHA 命令的参数

Lua 脚本内部执行 Redis 命令

redis WATCH/MULTI/EXEC 与Lua

Redis Lua 脚本管理

死锁

生产环境下部署


官网:

The Programming Language Lua

背景:

Redis 在 2.6 版本中推出了脚本功能,允许开发者将 Lua 语言编写的脚本传到 Redis 中执行。使用 Lua 脚本的优点有如下几点:

        减少网络开销:本来需要多次请求的操作,可以一次请求完成,从而节约网络开销;

        原子操作:Redis 会将整个脚本作为一个整体执行,中间不会执行其它命令;

        复用:客户端发送的脚本会存储在 Redis 中,从而实现脚本的复用。

第一个lua脚本

登录redis客户端,在redis的bin目录下启动redis-cli

eval "return 'hello world'" 0

带参数的脚本

输入:eval "local msg='hello world ' return msg..KEYS[1]..ARGV[1]" 1 key1 value1
 输出:"hello worldkey1key2"

lua的表是基于1的,也就是说索引以数值1开始。所以在表中的第一个元素就是mytable[1],第二个就是mytable[2]等等。 表中不能有nil值。如果一个操作表中有[1, nil, 3, 4],那么结果将会是[1],表将会在第一个nil截断。其中传参是第一个参数为要传的参数个数,接着传的是一个key-value的值,连着两个值组成一组参数,KEYS[1] 获取的是key值,ARGV[1] 获取的是value值。

Lua 脚本获取 EVAL & EVALSHA 命令的参数

通过 Lua 脚本的全局变量 KEYS 和 ARGV,能够访问 EVAL 和 EVALSHA 命令的 key [key ...] 参数和 arg [arg ...] 参数。

作为 Lua Table,能够将 KEYS 和 ARGV 作为一维数组使用,其下标从 1 开始。

Lua 脚本内部执行 Redis 命令

Lua 脚本内部可以通过内置函数调用redis:

redis.call();
redis.pcall();

两者的区别是:若redis 命令执行错误,redis.call() 将错误抛出;redis.pcall() 将错误内容返回。

redis WATCH/MULTI/EXEC 与Lua

redis 原生支持监听,事务和批处理,为什么还需要引入Lua?

  • 两者不存在竞争关系,而是增强关系,lua可以完成redis自身没有的功能
  • 在lua中可以使用上一步的结果,也就是可以开发后面操作依赖前面操作的执行结果的应用,MULT中的命令都是独立操作
  • redis可以编写模块增强功能,但是c语言写模块,太难了,lua简单的多
  • 计算向移动数据
  • 原子操作

lua脚本尽量短小并且尽量保证同一事物写在一段脚本内,因为redis是单线程的,过长的执行会造成阻塞,影响服务器性能。

Redis Lua 脚本管理

  1. script load 此命令用于将Lua脚本加载到Redis内存中
  2. script exists scripts exists sha1 [sha1 …] 此命令用于判断sha1是否已经加载到Redis内存中
  3. script flush 此命令用于清除Redis内存已经加载的所有Lua脚本,在执行script flush后,sha1不复存在
  4. script kill 此命令用于杀掉正在执行的Lua脚本

死锁

下面代码会进入死循环,导致redis无法接受其他命令

eval "while true do end" 0
127.0.0.1:6379> keys *
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

但是可以接受 SCRIPT KILL or SHUTDOWN NOSAVE. 两个命令

SHUTDOWN NOSAVE 不会进行持久化的操作

SCRIPT KILL 可以杀死正在执行的进程

生产环境下部署

加载脚本到redis

./redis-cli script load "$(cat test.lua)"

得到sha1值,执行

./redis-cli evalsha "210e9df70f9e56cde709b790d8e3e84f8398b8ee" 1 count

以下是test.lua脚本,用于计数

local msg='count:'
 local count = redis.call("get","count")
 if not count then
         redis.call("set","count",1)
 endredis.call("incr","count")
return msg..count+1