6、redis的事务和Lua脚本

  • Post author:
  • Post category:其他


一、redis中的事务

redis的一个事务要经过三个阶段:

1、

multi

开启一个事务

2、

命令入队

(此时只是简单的入队,没有被执行),

等待被执行

3、

exec 按入队的顺序执行所有命令

如果在

入队时命令本身发生了错误

(如set写成 sett,expire写成expired),则exec时整个事务都将作废;—》

全体连坐

如果在

入队时命令本身没写错,但是语法上出错如 incr 字符串

,则exec时只有该命令作废,其他命令正常。—》

冤头债主

问题:全体连坐的情况还行,但是冤头债主的情况就不符合事务的一致性了,而且此时redis还不能自动回滚,这样前后的数据就会出现不一致性,咋办?用watch监控。

watch监控(对冤头债主情况的一个补充和完善,使redis的事务符合RDB中的事务,类似乐观锁机制)


语法: watch key[key…]

作用:


监控一个或多个key(相当于给这些key上了把锁),如果该key在参与执行事务期间,其他客户端对其值进行了修改,那么本次事务将全部作废,后者的修改会生效。


exec执行完后,本次事务中加的锁将变得无效了。

5个命令

multi :开启一个事务

discard :放弃提交事务

exec :提交事务

watch :监控一个或多个key

unwatch :取消watch命令对所有key的监控。

二、Lua脚本


Lua是用C语言编写的高效、强大、轻量级的解释型脚本语言

,其设计目标就是作为嵌入式程序移植到其他应用程序中,简单来说就是

作为一个脚本语言来服务于其他的应用程序

,是1993年由巴西一个大学研究小组发明的。目前应用广泛,如暴雪公司的魔兽世界、愤怒的小鸟、niginx服务器等都在使用Lua脚本。

使用Lua有三个好处:

a)

Lua脚本在redis中是原子执行的

,因为redis是单线程执行,确保了该Lua脚本从开始到执行结束期间不会再有其他命令被执行。相当于这一组命令就是一个命令一样。

b)

Lua脚本相当于客户端新定义了一个redis命令

c)

可以实现多个命令的打包发送和执行,减少了网络传输的开销。

一般来说,Lua作为一个解释型语言,运行是需要解释器的(编译型语言,则需要编译器和运行器)。


正好redis从2.6版本开始内置的Lua的解释器,所以我们编写的Lua脚本可以直接交给redis服务端去执行。

1、Lua的基本语法

1)变量与数据类型:

Lua提供了四种数据类型:strings(字符串)、tables(表格)、numbers(数值)、booleans(布尔)


(a)strings字符串

如:local strings str = “world” #定义一个局部变量str,local用来定义局部变量,没有local则为全局变量


(b)tables表格(值为哈希或列表结构)

如:local tables user = {age = 28, name = “tome”} #定义一个哈希表

print(“user_1 age is ” .. user[“age”]) #打印字符串,

..是Lua中的字符串连接符号,作用同java中的+号


遍历哈希表需要用到Lua内置函数pairs

for key,value in pairs(user_1)

do

    print(key .. value)

end

2)循环与流程控制

首先定义一个数组

如:local tables myArray = {“redis”, “jedis”, true, 88.0} #在Lua中可以用tables类型来定义一个数组,但下标从1开始


(a)for循环

如:计算1到100的和,以end作为循环结束符

local int sum = 0
for i = 1,100
do
   sum+=i
end
print(sum) --5050

如:遍历上面的数组,在变量前加个#表示获取它的长度

for i = 1,#myArray
do
   print(i,myArray[i])
end

或者这样遍历,ipairs是Lua的内置函数,用于变量数组

for index,value in ipairs(myArray)
do
   print(index)
   print(value)
end


b)while循环

如:计算1到100的和

local int sum = 0
local int i = 0
while i <= 100
do
   sum = sum +i
   i++
end
print(sum)


(c)if-else流程控制

如:判断数组中是否包含jedis字符串,若包含则打印true并结束for循环

local tables myArray = {"redis", "jedis", true, 88.0}
for i = 1, #myArray
do
   if myArray[i] == "jedis"
   then
       print("true")
       break        #跳出for循环
   else
       --do nothing
   end             #if-else结束
end                #for循环结束

3)定义函数

语法糖:Lua的函数定义语法上类似js的函数定义。

function 函数名([参数])    # [] 表示参数可有可无
    -- do something
    return xxx            #return也可有可无
end                       #同样以end作为结束符号

如:定义一个计算数字和的函数

function getSum(a,b)
    local sum = a + b
    return sum
end
调用该函数并打印结果
print("结果为"..getSum(1,1))     

2、在redis中使用Lua

在redis中使用Lua脚本有两种方式


a)直接执行简单的Lua代码(redis的eval命令)

语法:

eval Lua代码 key的个数 key列表 参数列表

如:eval “return KEYS[1]” 1 username 返回username

eval “return KEYS[1]..’:’..ARGV[1]” 1 username tom 返回 username:tom,age:12

其中:1 表示有一个key

KEYS[1] 是占位符,表示取出key列表中的第一个key,

ARGV[1] 也是占位符,表示取出参数列表中第一个参数

b)执行Lua脚本文件(redis的evalsha命令)

流程是两步:1、先将Lua脚本文件加载进redis服务端的内存中(会一直存在,除非手动删除),redis会返回一个sha1摘要


redis-cli script load “$(cat lua_get.lua)”

2、以后根据该sha1摘要执行对应的redis服务端的Lua脚本即可。


evalsha 脚本 sha1 值 key 个数 key 列表 参数列表

#执行逻辑与eval一致

使用Lua访问redis


redis.call()函数

:该函数执行失败后,会返回错误信息。

如:resis.call(“set”,”hello”,”world”)

在redis中:eval “return redis.call(‘get’,KEYS[1])” 1 hello #返回world

redis.pcall()函数:该函数执行失败后,会忽略错误继续执行。

Redis如何管理Lua脚本

Redis提供了4个命令实现对Lua脚本的管理


a)script load:用于将Lua脚本内容加载到Redis内存中,便于下次复用

script load strScript


b)script exists:用于判断sha1对应的脚本是否已经加载到Redis内存中

scripts exists sha1 [sha2 … ] #返回已被加载的个数


c)script flush:清除redis已加载的所有Lua脚本

script flush


d)script kill:杀死正在执行的Lua脚本(Lua执行时长过长时用)

script kill

注意:当Lua脚本正在执行写操作时,script kill命令将无法杀死该脚本,此时要么等待,要么执行shutdown save停掉redis服务。

3、在jedis中使用Lua

三个重要方法:

Object eval(String strScript, int keyCount, String… params)     #直接执行Lua代码

Object evalsha(String sha1, int keyCount, String… params)     #根据sha1返回值,去执行对应的Lua脚本。

String scriptLoad(String strScript):加载Lua脚本内容,返回sha1值。若服务器端已加载完毕,则可使用evalsha直接根据sha1值调用。

对于Lua脚本文件

1、要么在服务器端手动去加载,得到sha1值。

2、要么在客户端将脚本文件的内容读取成字符串,再加载和执行。

上一篇:

5、redis的数据持久化

下一篇:7、redis的主从复制



版权声明:本文为qq_40925189原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。