lua-redis

简介

在Redis中应用Lua脚本在业务开发中是比拟常见的事件,应用Lua的长处有以下几点。

  1. 对于与屡次redis指令的发送,应用Lua脚本能够缩小网络的开销。当网络传输慢或者响应要求高的场景中尤为要害。
    Lua脚本能够将多个申请通过脚本模式一次进行解决,缩小网络的相干时延。Redis还提供了Pipeline来解决这个问题,
    然而在后面指令会影响前面指令逻辑的场景下,Pipeline并不能满足。
  2. 原子操作。在Lua脚本中会将整个脚本作为一个整体来执行,两头不会被其余指令而打断,因而保障了原子性
    因而在咱们写Lua脚本时,无需思考竞争而导致的整体数据状态不统一的问题,并且无需应用事务。并且因为此个性,
    需确保脚本尽可能不要运行工夫过长,要确保脚本执行的粒度最小化。
  3. 复用和封装。针对于一些通用能力的性能,把这些放到redis脚本中去实现。
    其余客户端调用雷同的脚本代码,从而达到逻辑的复用

比照Lua脚本与事务:

Lua脚本自身也能够看作为一种事务,而且应用脚本起来更简略,并且可管制的流程更灵便。

在应用Redis事务的时候会遇到两种问题

  • 事务在调用EXEC之前,产生了语法错误(如参数数量,参数名等问题)或者服务器内存等问题。
    遇到这一类问题是,会在服务器运行这些指令前发现这些问题(2.6.5之后),并且终止此次的事务。
  • 事务执行EXEC调用之后的失败,如事务中某个键的类型谬误的问题。两头指令的谬误并不会终止前面的流程,
    也不会导致后面指令的回滚。然而在Lua脚本中,你能够齐全管制这些。

Lua-Redis指令教程

注入和应用脚本:

  1. 运行脚本时把脚本发送到Redis(网络开销较大)

    EVAL script numkeys [key ...] [arg ...]# 运行脚本redis> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second# 运行只读脚本,脚本须要不蕴含任何批改内容的操作。这个指令能够随便被kill掉,# 而且不会影响到正本的stream。这个指令能够在master和replica上执行。redis> EVAL_RO "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
  2. 上传脚本,之后应用SHA1校验和来调用脚本。(潜在碰撞问题,在应用时个别会漠视)

    SCRIPT LOAD scriptEVALSHA sha1 numkeys key [key ...] arg [arg ...]redis> SCRIPT LOAD "return 'hello moto'""232fd51614574cf0867b83d384a5e898cfd24e5a"# 运行脚本redis> EVALSHA 232fd51614574cf0867b83d384a5e898cfd24e5a 0"hello moto"# 运行只读脚本,脚本须要不蕴含任何批改内容的操作。这个指令能够随便被kill掉,# 而且不会影响到正本的stream。这个指令能够在master和replica上执行。redis> EVALSHA 232fd51614574cf0867b83d384a5e898cfd24e5a 0
  3. 其余一些指令
  4. SCRIPT DEBUG 用来调试脚本 Document
  5. SCRIPT EXISTS 通过校验值用来查看脚本是否存在 Document
  6. SCRIPT FLUSH 革除脚本 Document
  7. SCRIPT KILL 停到当初执行中的脚本,默认脚本没有写操作 Document
  8. 留神点
  9. 运行脚本须要严格依照Keys和Args的要求来进行传参。
    所有操作到的redis key应放到Keys对象中,否则可能会影响到在redis集群中谬误体现。

    # Bad> eval "return redis.call('set','foo','bar')" 0OK# Good> eval "return redis.call('set',KEYS[1],'bar')" 1 fooOK

调用redis指令

在redis lua脚本中最罕用的就是调用redis原生的指令。有以下两个指令:

  1. redis.call(command, key, arg1, arg2...): 当调用产生谬误时,主动终止脚本,强制把相干Lua 谬误返回给客户端。
  2. redis.pcall(command, key, arg1, arg2...): 当调用产生谬误时,会进行谬误拦挡,并返回相干谬误。

当调用redis.call和redis.pcall指令时Redis Reply会转换为Lua类型,当Lua脚本返回时,会将Lua类型转换为Redis Reply。
因而这两种类型的转换是须要通晓的。能够浏览此文档理解Redis协定。Link

  • Redis integer reply: 如EXISTS...
  • Redis bulk reply: 如GET...
  • Redis multi bulk reply: 如LRANGE...
  • Redis status reply: 如SET...
  • Redis error reply: 指令谬误...
转换表
  • Redis回复类型转Lua类型转换表:

         Redis integer reply   ->   Lua number        Redis bulk reply   ->   Lua string  Redis multi bulk reply   ->   Lua table (may have other Redis data types nested)      Redis status reply   ->   Lua table with a single ok field containing the status       Redis error reply   ->   Lua table with a single err field containing the error    Redis Nil bulk reply   ->   Lua false boolean typeRedis Nil multi bulk reply   ->   Lua false boolean type
  • Lua类型类型转Redis回复转换表:

                  Lua number   ->   Redis integer reply (the number is converted into an integer)              Lua string   ->   Redis bulk reply       Lua table (array)   ->   Redis multi bulk reply (truncated to the first nil inside the Lua array if any)       Lua table with      ->   Redis status reply       a single ok field       Lua table with      ->   Redis error reply       a single err field       Lua boolean false   ->   Redis Nil bulk reply.
  • 注意事项
  1. Lua只有一个数字类型,Lua number。没有辨别integer和floats,因而将永远转换Lua numbers为integer回复
    如果须要floats类型,请return字符串。(ZSCORE指令就是这么实现的)
  2. 因为Lua语义起因,Lua array不能够有nils。当redis reply转换到Lua array时会终止运行
  3. Lua Table蕴含keys(和其values),转换成redis reply将不会蕴含keys
RESP3 - Redis 6 协定

如须要理解请查看官网文档 Link

示例

Link

Reference:

  • Redis Lua实战 Link
  • Redis 官网文档 Link