zoukankan      html  css  js  c++  java
  • redis 简单整理——Lua[十一]

    前言

    简单介绍一下Lua。

    正文

    为了保证多条命令组合的原子性,Redis提供了简单的事务功能以及集 成Lua脚本来解决这个问题。

    前面提及到pipline,也提及到pipline 并不是原子性的,如果多条命令想达到一定的原子性怎么破呢?

    多条语句达到原子性,一般而言,我们想到的会是事务。

    简单地说,事务表示一 组动作,要么全部执行,要么全部不执行。

    Redis提供了简单的事务功能,将一组需要一起执行的命令放到multi和 exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束,它们之间的命令是原子顺序执行的。

    multi命令代表事务开始,exec命令代表事务结束,它们之间的命令是原子顺序执行的。

    这里需要解释一下?

    前面的pipline是不是原子性的,为什么这么说呢? 因为前面pipline 也是将加入队列,但是不能保证原子性。

    如果要停止事务的执行,可以使用discard命令代替exec命令即可。

    如果事务中的命令出现错误,Redis的处理机制也不尽相同。

    这是我们开发需要关注的地方。

    1. 命令错误,这是比较幸运的错误。

    整个事务是不会去执行的,这种错误还是比较好解决。

    1. 运行时候错误

    就是说运行了第1、2条,然后第3条出错,或者写错了。

    可以看到Redis并不支持回滚功能,sadd user:a:follow user:b命令已 经执行成功,开发人员需要自己修复这类问题。

    同样即使我们没有发生错误,那么事务也是可能有问题的。

    思考一下mysql 有几种情况发生,什么不可重复读等。

    这些都是锁的问题,那么就看一下redis里面的锁吧。

    有些应用场景需要在事务之前,确保事务中的key没有被其他客户端修 改过,才执行事务,否则不执行(类似乐观锁)。

    Redis提供了watch命令来 解决这类问题。

    下面执行一下下面的流程,看下变化。

    然后你发现该事务没有执行成功。

    也就是说一但watch有变化,那么执行就不会进行下去。

    Redis提供了简单的事务,之所以说它简单,主要是因为它不支持事务 中的回滚特性,同时无法实现命令之间的逻辑关系计算,当然也体现了 Redis的“keep it simple”的特性.

    Lua语言提供了如下几种数据类型:booleans(布尔)、numbers(数 值)、strings(字符串)、tables(表格),和许多高级语言相比,相对简 单。下面将结合例子对Lua的基本数据类型和逻辑处理进行说明

    https://www.runoob.com/lua/lua-tutorial.html

    上面可以去看看,很快就能学会。

    那么看下Lua语言对redis的处理。

    1. 看下在redis中如何执行Lua语言。

    在Redis中执行Lua脚本有两种方法:eval和evalsha。

    eval 脚本内容 key个数 key列表 参数列表
    

    除了使用eval,Redis还提供了evalsha命令来执行Lua脚本。

    首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和, evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送 Lua脚本的开销。这样客户端就不需要每次执行脚本内容,而脚本也会常驻 在服务端,脚本功能得到了复用。

    加载脚本:script load命令可以将脚本内容加载到Redis内存中,例如下 面将lua_get.lua加载到Redis中,得到SHA1 为:"7413dc2440db1fea7c0a0bde841fa68eefaf149c"

    执行脚本:evalsha的使用方法如下,参数使用SHA1值,执行逻辑和 eval一致。

    有点像存储过程。

    Lua的Redis API:

    Lua可以使用redis.call函数实现对Redis的访问,例如下面代码是Lua使用 redis.call调用了Redis的set和get操作:

    redis.call("set", "hello", "world") redis.call("get", "hello")
    
    eval 'return redis.call("get", KEYS[1])' 1 hello "world"
    

    除此之外Lua还可以使用redis.pcall函数实现对Redis的调用,redis.call和 redis.pcall的不同在于,如果redis.call执行失败,那么脚本执行结束会直接返 回错误,而redis.pcall会忽略错误继续执行脚本,所以在实际开发中要根据 具体的应用场景进行函数的选择。

    开发提示:

    Lua可以使用redis.log函数将Lua脚本的日志输出到Redis的日志文件中, 但是一定要控制日志级别。 Redis3.2提供了Lua Script Debugger功能用来调试复杂的Lua脚本,具体 可以参考:http://redis.io/topics/ldb。
    

    案例

    Lua脚本功能为Redis开发和运维人员带来如下三个好处:
    ·Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
    ·Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这 些命令常驻在Redis内存中,实现复用的效果。
    ·Lua脚本可以将多条命令一次性打包,有效地减少网络开销。

    案例:

    实现:

    管理Lua 脚本

    Redis提供了4个命令实现对Lua脚本的管理,下面分别介绍。

    1. script load script

    2. script exists

    此命令用于判断sha1是否已经加载到Redis内存中.

    script exists a5260dd66ce02462c5b5231c727b3f7772c0bcc5
    

    这样就说明没有被加载进去。

    1. script flush

    此命令用于清除Redis内存已经加载的所有Lua脚本

    1. script kill

    此命令用于杀掉正在执行的Lua脚本。如果Lua脚本比较耗时,甚至Lua 脚本存在问题,那么此时Lua脚本的执行会阻塞Redis,直到脚本执行完毕或 者外部进行干预将其结束。

    模拟一下。

    eval 'while 1==1 do end' 0
    

    Redis提供了一个lua-time-limit参数,默认是5秒,它是Lua脚本的“超时 时间”,但这个超时时间仅仅是当Lua脚本时间超过lua-time-limit后,向其他 命令调用发送BUSY的信号,但是并不会停止掉服务端和客户端的脚本执 行,所以当达到lua-time-limit值之后,其他客户端在执行正常的命令时,将 会收到“Busy Redis is busy running a script”错误,并且提示使用script kill或者 shutdown nosave命令来杀掉这个busy的脚本

    此时Redis已经阻塞,无法处理正常的调用,这时可以选择继续等待, 但更多时候需要快速将脚本杀掉。使用shutdown save显然不太合适,所以选 择script kill,当script kill执行之后,客户端调用会恢复.

    那么kill 是否一定能成功呢? 不一定。

    有一点需要注意,如果当前Lua脚本正在执行写操作,那么script kill将不会生效。

    上面提示Lua脚本正在向Redis执行写命令,要么等待脚本执行结束要么 使用shutdown save停掉Redis服务。可见Lua脚本虽然好用,但是使用不当破 坏性也是难以想象的。

    这种算是灾难模式。

    下一节bitmaps,这个还是比较关键的,二进制存储,很多业务上需要。

  • 相关阅读:
    成功更容易光顾磨难和艰辛,正如只有经过泥泞的道路才会留下脚印
    只要信心在,勇气就在,努力在,成功就在!
    不积跬步无以至千里,不积小流无以成江海
    你给自己留的退路越多,你失败的可能性就越大
    不要质疑你的付出,这些都会是一种累积一种沉淀,它们会默默铺路,只为让你成为更优秀的人
    生活的一大乐趣便是完成别人认为你不能做到的事情
    优于别人,并不高贵,真正的高贵,是优于过去的自己
    再长的路 ,一步步也能走完,再短的路,不迈开双脚也不无法到达!
    09SpringAopAdvice
    java中接口(interface)和虚基类(abstract class)的区别
  • 原文地址:https://www.cnblogs.com/aoximin/p/15169716.html
Copyright © 2011-2022 走看看