一、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的主从复制