• Redis(六)Lua脚本的支持


    Redis为什么需要Lua脚本的支持

    当应用需要Redis完成一些Redis命令不支持的特性时,要么扩展Redis client或者更甚至编写c扩展Redis server。这都大大造成了应用的实现的难度。在此基础上,Redis通过内置Lua解释器,Redis client可以发起执行Lua脚本,完成特殊的功能需求。

    Redis中使用Lua脚本

    在Redis中可以通过使用eval和evalsha命令提供对执行Lua的支持。

    eval语法:

    EVAL script numkeys key [key ...] arg [arg ...]

    • script是lua脚本;
    • numkeys是lua脚本中key的数量;
    • key是多选,即lua脚本中可以操作多个key;
    • arg是多选,供lua基本执行时提供参数;

    在lua脚本中可以通过全局变量KEYS和ARGV获取key和arg。KEYS和ARGV都是数组,KEYS[1],KEYS[2],...等等,基数从1开始,按照顺序获取后面的key。ARGV同理。

    如:

    > eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
    1) "key1"
    2) "key2"
    3) "first"
    4) "second"
    

    上面返回key1和key2的value,并且返回连个参数。

    Redis提供了两个不同的Lua函数调用执行Redis命令:

    • redis.call():当redis命令执行错误时,将会返回错误;
    • redis.pcall:当redis命令执行错误时,将会捕获异常,返回一个带有错误域的Lua表;

    如:

    127.0.0.1:6380>  lpush foo a
    (integer) 1
    
    127.0.0.1:6380> eval "return redis.call('get','foo')" 0
    (error) ERR Error running script (call to f_6b1bf486c81ceb7edf3c093f4c48582e38c0e791): @user_script:1: WRONGTYPE Operation against a key holding the wrong kind of value
    
    127.0.0.1:6380> EVAL "return redis.pcall('get', 'foo')" 0
    (error) WRONGTYPE Operation against a key holding the wrong kind of value
    
    

    Lua脚本作为一种脚本语言,有自己的数据类型,Redis也有自己的数据类型。在Lua脚本和Redis命令操作的之间切换时,必然会涉及数据类型的转化。

    数据类型之间的转换遵循这样一个设计原则:Redis调用Lua解释器执行脚本时,会将Redis类型转化成Lua类型;当Lua脚本执行后,返回值时,会将返回值转化成Redis类型,eval再将Lua脚本返回给client。

    具体的对应细节参考:Lua 数据类型和 Redis 数据类型之间转换

    Redis中Lua脚本带来的收益

    原子性:Redis使用单个Lua解释器去运行所有脚本,并且Redis也保证脚本会以原子性的方式执行:当某个脚本正在运行的时候,不会有其他脚本或Redis命令被执行。这和使用MULTI/EXEC包围的事务很类似。在其他别的客户端看来,脚本的效果要么是不可见的,要么就是已完成的。另一方面,这也意味着,执行一个运行缓慢的脚本并不是一个好主意。写一个跑得很快很顺溜的脚本并不难,因为脚本的运行开销非常少,但是当你不得不使用一些跑得比较慢的脚本时,请小心,因为当这些蜗牛脚本在慢吞吞地运行的时候,其他客户端会因为服务器正忙而无法执行命令。

    缓存脚本:eval命令要求每次每次执行lua脚本时,都需要发送lua脚本至服务器,虽然redis缓存机制保证不会重新编译lua脚本,但是每次都传输脚本主体,无疑是消耗带框。为了减少带框,Redis使用evalsha命令发起lua脚本,但是evalsha的第一个参数不是lua脚本,而是脚本所对应的shasum校验和值。

    如:

    127.0.0.1:6380> set foo bar
    OK
    127.0.0.1:6380> 
    127.0.0.1:6380> 
    127.0.0.1:6380> eval "return redis.call('get','foo')" 0
    "bar"
    127.0.0.1:6380> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
    "bar"
    
    

    客户端库的底层实现可以一直乐观地使用 EVALSHA 来代替 EVAL ,并期望着要使用的脚本已经保存在服务器上了,只有当 NOSCRIPT 错误发生时,才使用 EVAL 命令重新发送脚本,这样就可以最大限度地节省带宽。

    Redis提供的SCRIPT命令

    • script flush:清除所有脚本缓存;
    • script exists:根据给定的脚本校验和,判断脚本是否存在;
    • script load:将一个脚本载入内存,但是不运行;
    • script kill:杀死一个正在执行的脚本程序;
    参考

    https://redis.io/commands/eval

  • 相关阅读:
    来自极客标签10款最新设计素材系列七
    支持触摸设备的响应式HTML5音频播放器 AudioPlayer.js
    最流行的JavaScript库,jQuery不再支持IE旧版本
    来自极客标签10款最新设计素材系列六
    来自极客标签10款最新设计素材系列四
    推荐10款来自极客标签的超棒前端特效[第四期]
    响应式的无限滚动全屏dribbble作品集布局展示效果
    推荐10款来自极客标签的超棒前端特效[第五期]
    基于属性编程
    郁闷的企业软件开发
  • 原文地址:https://www.cnblogs.com/lxyit/p/9829079.html
Copyright © 2020-2023  润新知