资讯详情

Redis与Lua详解

Lua

Lua语法

Lua 数据类型

Lua 是,变量不需要类型定义,只需要赋值变量。 该值可以作为参数传输或结果返回存储在变量中。

Lua 中有 8 基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

数据类型 描述
nil 这是最简单的,只有值nil属于这一类,表示无效值(相当于条件表达式false)。
boolean 包括两个值:false和true。
number 实浮点数表示双精度类型
string 字符串由一对双引号或单引号表示。 2 个方括号 [[ ]]"来表示"一块"字符串。字符串连接使用 ..
function 由 C 或 Lua 编写的函数
userdata 变量中任意存储的C数据结构
thread 用于执行协同程序的独立线路
table Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。 Lua 里,table 创建是通过的"构造表达式"完成最简单的结构表达式{},用来创建一个空表。

通过type()该方法可获得变量类型,返回值为的。

type(X) print("a" .. 'b')   --字符串连接  

Lua字符串

字符串或串(String)它是一串由数字、字母和下划线组成的字符。

Lua 以下三种方符串可以用以下三种方式串:

  • 单引号之间的一串字符。

  • 双引号之间的一串字符。

  • 一串字符。

字符串转义

和其他语言一样,使用它\

字符串函数
-- 所有字符串都转换为大写字母。 string.upper(argument) -- 所有字符串都转换成小写字母。 string.lower(argument) --替换字符串。 ----mainString 要操作的字符串, findString 替换字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换) string.gsub(mainString,findString,replaceString,num) --在指定的目标字符串中搜索指定的内容(第三个参数为索引),并返回其具体位置。如果不存在,则返回 nil。 string.find (str, substr, [init, [end]]) --字符串反转 string.reverse(arg) --返回类似printf的格式化字符串 string.format(...) --char 将整形数字转换成字符并连接, byte 转换字符为整数值(可指定一个字符,默认为第一个字符)。 string.char(...) 和 string.byte(arg[,int])
--计算字符串长度。
string.len(arg)
--返回字符串string的n个拷贝(重复n次)
string.rep(string, n)
--链接两个字符串
..
--返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
string.gmatch(str, pattern)
--只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
--在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
string.match(str, pattern, init)
--截取字符串。j:截取结束位置,默认为 -1,最后一个字符。
string.sub(s, i [, j])

Lua 变量

变量在使用前,需要在代码中进行声明,即创建该变量。

Lua 变量有三种类型:

Lua 中的变量(与JavaScript类似)

局部变量的作用域为从声明位置开始到所在语句块结束。

变量的 nil

在Lua 语言中,,使用未经初始化的全局变量不会导致错误。当使用未经初始化的全局变量时,得到的结果为

当把nil赋值给全局变量时,Lua会回收该全局变量。

赋值语句

a = "hello" .. "world"
-- Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
a, b = 10, 2*x       --> a=10; b=2*x
-- 遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
x, y = y, x                     -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i]         -- swap 'a[i]' for 'a[j]'

当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:

  • a. 变量个数 > 值的个数 按变量个数补足nil

  • b. 变量个数 < 值的个数 多余的值会被忽略

*索引

对 table 的索引使用方括号 []。Lua 也提供了 . 操作。

t[i]
t.i                 -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用

语句控制

循环语句

-- 
while(condition)
do
   statements
end
-- 数值循环:var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。
for var=exp1,exp2,exp3 do  
    <执行体>  
end  
--泛型for 循环。i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
a = { 
        "one", "two", "three"}
for i, v in ipairs(a) do
    print(i, v)
end 

-- 类似其他语言 do ... while。
repeat
   statements
until( condition )

--嵌套循环。前面几个循环的互相嵌套
while(condition)
do
   while(condition)
   do
      statements
   end
   statements
end

if

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

函数

通过关键字 function 声明一个函数。return 语句返回结果。

optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
    function_body
    return result_params_comma_separated
end

说明:

  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local
  • function_name: 指定函数名称。
  • argument1, argument2, argument3..., argumentn: 函数参数,多个参数以隔开,函数也可以不带参数。
  • function_body: 函数体,函数中需要执行的代码语句块。
  • result_params_comma_separated: 函数返回值,

*多值返回

function maximum (a)
    local mi = 1             -- 最大值索引
    local m = a[mi]          -- 最大值
    for i,val in ipairs(a) do
       if val > m then
           mi = i
           m = val
       end
    end
    return m, mi
end

print(maximum({ 
        8,10,23,12,5}))

*可变参数

Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 表示函数有可变的参数。

function add(...)  
local s = 0  
  for i, v in ipairs{ 
        ...} do   --> {...} 表示一个由所有变长参数构成的数组 。inpairs没有使用括号,应该是语法编译。
    s = s + v  
  end  
  return s  
end  
print(add(3,4,5,6,7))  --->25

Lua运算符

算术运算符

设定 A 的值为10,B 的值为 20:

操作符 描述 实例
+ 加法 A + B 输出结果 30
- 减法 A - B 输出结果 -10
* 乘法 A * B 输出结果 200
/ 除法 B / A 输出结果 2
% 取余 B % A 输出结果 0
^ 乘幂 A^2 输出结果 100
- 负号 -A 输出结果 -10

关系运算符

设定 A 的值为10,B 的值为 20:

操作符 描述 实例
== 等于,检测两个值是否相等,相等返回 true,否则返回 false (A == B) 为 false。
~= 不等于,检测两个值是否相等,不相等返回 true,否则返回 false (A ~= B) 为 true。
> 大于,如果左边的值大于右边的值,返回 true,否则返回 false (A > B) 为 false。
< 小于,如果左边的值大于右边的值,返回 false,否则返回 true (A < B) 为 true。
>= 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false (A >= B) 返回 false。
<= 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false (A <= B) 返回 true。

逻辑运算符

设定 A 的值为 true,B 的值为 false:

操作符 描述 实例
and 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 (A and B) 为 false。
or 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 (A or B) 为 true。
not 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 not(A and B) 为 true。

*其他运算符

操作符 描述 实例
连接两个字符串 a…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”。
一元运算符,返回字符串或表的长度。 #“Hello” 返回 5

运算符优先级

从高到低的顺序:

^
not    - (unary)
*      /       %
+      -
..                                                ####################
<      >      <=     >=     ~=     ==
and
or

*

a+i < b/2+1          --> (a+i) < ((b/2)+1)
5+x^2*8              --> 5+((x^2)*8)
a < y and y <= z     --> (a < y) and (y <= z)
-x^2                 --> -(x^2)
x^y^z                --> x^(y^z) ::::右结合的:::,注意顺序

Lua数组

Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。

* 一维数组

array = { 
        "Lua", "Tutorial"}

在 Lua 索引值是以 1 为起始,但你指定 0 开始。

还可以以为数组索引值。

array = { 
        }

for i= -2, 2 do
   array[i] = i *2
end

for i = -2,2 do
   print(array[i])
end
--OUTPUT:
-4
-2
0
2
4

多维数组

就是类似于value是一个map的map。

-- 初始化数组
array = { 
        }
for i=1,3 do
   array[i] = { 
        }
      for j=1,3 do
         array[i][j] = i*j
      end
end

Lua迭代器

泛型 for 迭代器

泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。

for k, v in pairs(t) do
    print(k, v)
end

泛型 for 的执行过程:

  • 首先,初始化,计算 in 后面的值,表达式应该返回泛型 for 需要的三个值:;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
  • 第二,将状态常量和控制变量作为调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
  • 第三,将迭代函数返回的值赋给
  • 第四,如果返回的第一个值为nil循环结束,否则执行循环体。
  • 第五,回到第二步再次调用迭代函数

在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua 的迭代器包含以下两种类型:

  • 无状态的迭代器
  • 多状态的迭代器

无状态的迭代器

无状态的迭代器是指的迭代器,因此在循环中我们可以利用无状态迭代器花费额外的代价。

每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。

这种无状态迭代器的典型的简单的例子是 ipairs,它遍历数组的每一个元素,元素的索引需要是数值。

--迭代函数
function square(iteratorMaxCount,currentNumber)
   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end

for i,n in square,3,0
do
   print(i,n)
end

--OUTPUT:
1    1
2    4
3    9

迭代的状态包括被遍历的表(循环过程中不会改变的)和当前的索引下标(),ipairs 和都很简单,我们在 Lua 中可以这样实现:

--迭代函数。参数a:是状态常量,i:控制变量
function iter (a, i)
    i = i + 1
    local v = a[i]
    if v then
       return i, v
    end
end
 
function ipairs (a)
    return iter, a, 0
end

当 Lua 调用 ipairs(a) 开始循环时,他获取三个值:;然后 Lua 调用 iter(a,0) 返回 1, a[1](除非 a[1]=nil);第二次迭代调用 iter(a,1) 返回 2, a[2]……直到第一个 nil 元素。

多状态的迭代器

迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 table 作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在 table 内,所以迭代函数通常不需要第二个参数。

array = { 
        "Google", "Runoob"}

function elementIterator (collection)
   local index = 0
   local count = #collection   --collectioin 长度
   -- 闭包函数
   return function ()
      index = index + 1
      if index <= count
      then
         -- 返回迭代器的当前元素
         return collection[index]
      end
   end
end

for element in elementIterator(array)  -- elementIterator(array) 调用的返回值(闭包) ,作为一个迭代函数。
do
   print(element)
end
--OUTPUT:
Google
Runoob

Lua table(表)

table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。

Lua table 使用关联型数组,你可以用的值来作数组的索引,但这个值

Lua table 是不固定大小的,你可以根据自己需要进行扩容。

Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。

table(表)的构造

-- 初始化表
mytable = { 
        }

-- 指定值
mytable[1]= "Lua"

-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存

Table 操作

序号 方法 用途
1 table.concat (table [, sep [, start [, end]]]): concat是concatenate(连, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。
2 table.insert (table, [pos,] value): 在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
3 table.maxn (table) 指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。()
4 table.remove (table [, pos]) 返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。
5 table.sort (table [, comp]) 对给定的table进行升序排序。

Lua其他

其他 Redis用不不涉及,就不整理了。

参考

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

Redis

Redis中使用Lua的好处

  • 减少网络开销。可以将多个请求通过脚本的形式,减少网络时延
  • 。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
  • 。客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。

Redis Lua脚本与事务

从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。

使用事务时可能会遇上以下两种错误:

  • 事务在执行 EXEC 之前,。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
  • 命令可能在 失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。

对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED ,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。

从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。

经过测试lua中发生异常处理方式和redis 事务一致,可以说这两个东西是一样的,但是lua支持缓存,可以复用脚本,这个是原来的事务所没有的

Redis 相关命令

EVAL

EVAL script numkeys key [key …] arg [arg …]

script 参数是一段 Lua 5.1 脚本程序,它会被运行在 Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。

numkeys 参数用于指定键名参数的个数。

键名参数 key [key ...]EVAL参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1]KEYS[2] ,以此类推)。

在命令的最后,那些不是键名参数的附加参数 arg [arg ...] ,可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1]ARGV[2] ,诸如此类)。

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

EVALSHA

EVAL 命令要求在每次执行脚本的时候脚本主体(script body)。Redis 有一个内部的缓存机制,因此它不会每次都重新编译脚本,不过在很多场合,付出无谓的带宽来传送脚本主体并不是最佳选择。

为了减少带宽的消耗, Redis 实现了 EVALSHA 命令,它的作用和 EVAL 一样,都用于对脚本求值,但它接受的(sum)。

EVALSHA 命令的表现如下:

  • 如果服务器还缓存了给定的 SHA1 校验和所指定的脚本,那么执行这个脚本
  • 如果服务器未缓存给定的 SHA1 校验和所指定的脚本,那么它返回一个特殊的错误,提醒用户使用 EVAL 代替 EVALSHA

以下是示例:

> set foo bar
OK

> eval "return redis.call('get','foo')" 0
"bar"

> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"

> evalsha ffffffffffffffffffffffffffffffffffffffff 0
(error) `NOSCRIPT` No matching script. Please use [EVAL](/commands/eval).

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

这也说明了执行 EVAL 命令时,使用正确的格式来传递键名参数和附加参数的重要性:因为如果将参数硬写在脚本中,那么每次当参数改变的时候,都要重新发送脚本,即使脚本的主体并没有改变,相反,通过使用正确的格式来传递键名参数和附加参数,就可以在脚本主体不变的情况下,直接使用 EVALSHA 命令对脚本进行复用,免去了无谓的带宽消耗。

SCRIPT FLUSH

清除Redis服务端所有 Lua 脚本缓存

script flush

SCRIPT EXISTS

给定一个或多个脚本的 SHA1 校验和,返回一个包含 0或 1 的列表,表示校验和所指定的脚本是否已经被保存在缓存当中

SCRIPT EXISTS sha1 [sha1 …] 

SCRIPT LOAD

将脚本 script 添加到Redis服务器的脚本缓存中,并不立即执行这个脚本,而是会立即对输入的脚本进行求值。并返回给定脚本的 SHA1 校验和。如果给定的脚本已经在缓存里面了,那么不执行任何操作。

SCRIPT LOAD script 

SCRIPT KILL

杀死当前的脚本

SCRIPT KILL

杀死当前正在运行的 Lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效。 这个命令主要用于终止运行时间过长的脚本,比如一个因为 BUG 而发生无限 loop 的脚本,诸如此类。

假如当前正在运行的脚本已经执行过写操作,那么即使执行SCRIPT KILL,也无法将它杀死,因为这是违反 Lua 脚本的原子性执行原则的。在这种情况下,唯一可行的办法是使用SHUTDOWN NOSAVE命令,通过停止整个 Redis 进程来停止脚本的运行,并防止不完整(half-written)的信息被写入数据库中。

Lua中执行redis命令

在 Lua 脚本中,可以使用两个不同函数来执行 Redis 命令,它们分别是:

  • redis.call()
  • redis.pcall()

这两个函数的在于它们使用不同的方式

redis.call()redis.pcall() 两个函数的参数可以是任何格式良好(well formed)的 Redis 命令:

> eval "return redis.call('set','foo','bar')" 0
OK

上面这段脚本的确实现了将键 foo 的值设为 bar 的目的,但是,它违反了 EVAL 命令的语义,因为脚本里使用的所有键都应该由 KEYS 数组来传递,就像这样:

> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK

要求使用正确的形式来传递键(key)是有原因的,因为不仅仅是 EVAL 这个命令,所有的 Redis 命令,,籍此来确定命令会对哪些键进行操作。

因此,对于 EVAL 命令来说,必须使用,才能确保分析工作正确地执行。除此之外,使用正确的形式来传递键还有很多其他好处,它的一个特别重要的用途就是确保 Redis 集群可以将你的请求发送到。(对 Redis 集群的工作还在进行当中,但是脚本功能被设计成可以与集群功能保持兼容。)不过,这条规矩并不是强制性的,从而使得用户有机会滥用(abuse) Redis 单实例配置(single instance configuration),代价是这样写出的脚本不能被 Redis 集群所兼容。

在 Lua 数据类型和 Redis 数据类型之间转换

当 Lua 通过 call()pcall() 函数执行 Redis 命令的时候,命令的返回值会被转换成 Lua 数据结构。同样地,当 Lua 脚本在 Redis 内置的解释器里运行时,Lua 脚本的返回值也会被转换成 Redis 协议(protocol),然后由 EVAL 将值返回给客户端。

数据类型之间的转换遵循这样一个设计原则:如果将一个 Redis 值转换成 Lua 值,之后再将转换所得的 Lua 值转换回 Redis 值,那么这个转换所得的 Redis 值应该和最初时的 Redis 值一样。

换句话说,

以下列出的是详细的转换规则:

从 Redis 转换到 Lua :

Redis integer reply -> Lua number                       # Redis 整数转换成 Lua 数字
Redis bulk reply -> Lua string                              # Redis bulk 回复转换成 Lua 字符串
Redis multi bulk reply -> Lua table (may have other Redis data types nested)       #Redis 多条 bulk 回复转换成 Lua 表,表内可能有其他别的 Redis 数据类型
Redis status reply -> Lua table with a single ok field containing the status            #Redis 状态回复转换成 Lua 表,表内的 `ok` 域包含了状态信息
Redis error reply -> Lua table with a single err field containing the error                #Redis 错误回复转换成 Lua 表,表内的 `err` 域包含了错误信息
Redis Nil bulk reply and Nil multi bulk reply -> Lua false boolean type                 # Redis 的 Nil 回复和 Nil 多条回复转换成 Lua 的布尔值 `false`

从 Lua 转换到 Redis:

Lua number -> Redis integer reply # Lua 数字转换成 Redis 整数
Lua string -> Redis bulk reply # Lua 字符串转换成 Redis bulk 回复
Lua table (array) -> Redis multi bulk reply # Lua 表(数组)转换成 Redis 多条 bulk 回复
Lua table with a single ok field -> Redis status reply # 一个带单个 `ok` 域的 Lua 表,转换成 Redis 状态回复
Lua table with a single err field -> Redis error reply # 一个带单个 `err` 域的 Lua 表,转换成 Redis 错误回复
Lua boolean false -> Redis Nil bulk reply # Lua 的布尔值 `false` 转换成 Redis 的 Nil bulk 回复

从 Lua 转换到 Redis 有一条额外的规则,这条规则没有和它对应的从 Redis 转换到 Lua 的规则:

Lua boolean true -> Redis integer reply with value of 1  # Lua 布尔值 `true` 转换成 Redis 整数回复中的 `1`

示例:

#将 Lua 值转换成 Redis 值
> eval "return 10" 0
(integer) 10

> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 3
   2) "Hello World!"
#将 Redis 值转换成 Lua 值,然后再将 Lua 值转换成 Redis 值的类型转过程。
> eval "return redis.call('get','foo')" 0
"bar"

错误处理

标签: 0228连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台