每周一总结 总结(2) redis lua相关

2020-09-08 17:21:02 浏览数 (1)

最近看到有在缓存扣库存的操作,大致了解了下redis lua原子操作。这里大致记录和总结下了解到的内容。

什么是lua

Lua是一种功能强大、高效、轻量级、可嵌入的脚本语言。支持过程编程、面向对象编程、函数编程、数据驱动编程和数据描述等。 Lua将简单的过程语法和 基于关联数组和可扩展语义的强大数据描述相结合。是一种动态类型化的语言。通过使用基于寄存器的虚拟机解释字节码来运行,并且具有自动内存管理和增量动态垃圾回收功能,使其成为配置、脚本和快速原型的理想选择。

这里也只是简单了解了下lua的基本语法。日常开发中接触最多的语言是java,这里暂时列举下自己看到lua基本语法与java中不太相似的地方,更多的用法需要后续继续学习。

基础语法小差异

1. 注释

代码语言:javascript复制
-- 单行注释

--[[
  多行注释
--]]

2. 变量默认是全局的,需要局部变量需要使用关键字local,访问未初始化的变量返回结果是nil

3. lua基础数据类型。nil、boolean、number、string、function、userdata、thread、table。

  • 未赋值的变量即为nil,要删除某个变量也可以将它赋值为nil
  • type(X)==nil为false,原因是type(type(x))==string,type(X)==“nil”为true,
  • nil是false 0是true
  • 字符串 单引号'字符串' 双引号“字符串” 两个方括号[[一块字符串]]
  • lua中最重要的thread是协同程序coroutine,和线程差不多,拥有自己独立的栈、局部变量和指令指针。线程跟协程的区别:可以同时有多个线程运行、但是同时只能有一个协程运行。处于运行状态的协程只有被挂起时才会暂停。
  • 数组默认起始索引是1,可以从负值开始

4. lua变量:全局变量、局部变量、表中的域。默认为全局变量,只有带了local才是局部变量。局部变量作用于从声明位置开始到所在语句块结束。尽可能使用局部变量,可以避免命名冲突,同时也是由于访问局部变量的速度比访问全局变量的速度快。(原因访问局部变量更快的原因)

代码语言:javascript复制
-- 交换变量的值,原因:会先计算右边所有的值然后再执行赋值操作
x, y = y, x                     -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i]         -- swap 'a[i]' for 'a[j]'

-- 可以同时为多个变量赋值
a,b=5,10  -- a=5 ;b=10

-- 变量个数大于赋值个数,则缺少的部分由nil补充
a,b,c=5,10 -- a=5; b=10; c=nil

--变量数小于赋值个数,多余的参数被忽略

5. 循环的几种写法

代码语言:javascript复制
-- 1
while(condition)
do
   statements
end


-- 2
-- 从exp1 到exp2 步长为exp3, exp3可以没有,默认为1
for var=exp1,exp2,exp3 do  
    <执行体>  
end  

-- 3
repeat
   statements
until( condition )

6. if语句

代码语言:javascript复制
-- if 
if(布尔表达式)
then
   --[ 在布尔表达式为 true 时执行的语句 --]
end


-- if else
if(布尔表达式)
then
   --[ 布尔表达式为 true 时执行该语句块 --]
else
   --[ 布尔表达式为 false 时执行该语句块 --]
end


-- if elseif else
if( 布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]

elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]

elseif( 布尔表达式 3)
then
   --[ 在布尔表达式 3 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

lua的好处

来自于http://www.lua.org/about.html 仅供参考。

lua是一种经过验证的、健壮的语言。被用于很多工业系统,尤其是用于嵌入式领域以及游戏领域。

lua是脚本语言中执行速度最快的语言(Lua 的速度为什么比 Python 快?作者是个菜鸟,先放个链接,以后来学习)

lua可以比较容易的嵌入到其他语言中,比如java等,甚至也可以嵌入到其他脚本语言中

redis中的lua

最近想使用缓存扣库存的方式,然后发现了很多建议使用lua脚本的文章。

在redis中使用lua的好处

  • 原子操作
  • 高性能
  • 可复用

原子性

redis中使用lua为原子性的原因:

Atomicity of scripts Redis uses the same Lua interpreter to run all the commands. Also Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed. This semantic is similar to the one of MULTI / EXEC. From the point of view of all the other clients the effects of a script are either still not visible or already completed. However this also means that executing slow scripts is not a good idea. It is not hard to create fast scripts, as the script overhead is very low, but if you are going to use slow scripts you should be aware that while the script is running no other client can execute commands. https://redis.io/commands/eval

multi/exec:multi标记redis事务的开始,exec执行事务中的语句。

高性能

嵌入lua脚本之后,可以减少多个命令执行的网络开销

可复用

lua脚本保存在redis中,客户端都可以使用

redis执行lua脚本

代码语言:javascript复制
EVAL script numkeys key [key ...] arg [arg ...] 

// Evalsha 命令根据给定的 sha1 校验码,执行缓存在服务器中的脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...] 

redis call 和pcall

lua脚本中获取redis的数据或者执行redis的方法,call 和pcall

代码语言:javascript复制
-- 自己代码中遇到的几个例子

-- 判断是否存在keys[1],调用了redis的 exists函数,如果存在,则返回1,如果不存在则返回0
redis.call('exists', KEYS[1]) 

-- 获取keys[1]的值
redis.call('get', KEYS[1])

-- 针对keys[1] incrby num
redis.call('incrby', KEYS[1], num)

两个函数基本一样,唯一不同的是如果Redis命令调用出错的话,redis.call()将返回具体的错误信息,redis.pcall()将返回Lua table的包装错误。错误返回的示例

redis和lua的类型转换以及常见的坑

lua 有个类型是number,包含了整数和浮点数,如果在lua脚本中返回了小数,那么在redis中最终获取到的就是丢失了小数部分的结果。如果需要返回浮点数,返回结果转换成string之后再返回。

lua可以一次性返回多个结果,但是如果返回结果中包含nil,那么nil以及之后的结果都会获取失败

代码语言:javascript复制
-- 带有nil的返回
-- 可以获取到的结果 1,2,'2.2','foo'
return 1,2.2 '2.2','foo',nil,'other' 

redis 脚本中必须使用局部变量

什么情况下使用lua脚本,什么类型的lua脚本不建议使用

扩充若干指令原子性执行

耗时较长的脚本不建议

什么情况下使用redis事务,什么情况下使用lua脚本

A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.

目前的理解较为简单,尽可能使用lua脚本而非事务。在实际开发中暂未用过分布式事务。

pipeline、事务、lua脚本

pipiline:一次性执行多条指令,多条指令之间无相互影响。

pipeline是一次性执行多条指令,一次网络开销

事务是N次网络开销执行N次指令

pipeline和事务中的后置的指令都无法获取到前置指令的结果。

事务中multi开始,多条指令入队,在exec之后,开始执行,执行之后一次性返回多条指令的结果。

需要在执行过程中获取到前置指令的结果需要使用lua脚本。

0 人点赞