• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

redis 简单整理——Lua[十一]

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

前言

简单介绍一下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,这个还是比较关键的,二进制存储,很多业务上需要。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Lua 的问题发布时间:2022-07-22
下一篇:
lua的split函数发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap