Redis----lua篇

Redis Lua篇

相信大部分redis使用者,至少听过Lua脚本,使用的话,更好了,今天我以个人学习经验交流把我对lua的使用写出来。

首先看一下网上对lua脚本的介绍和使用:
run以上是在菜鸟教程上找到的内容并截图。

教程里并没有具体的写出使用,至少很简单的描述了一个实例。

比如:
怎么设置key?
怎么设置分布式锁?
怎么删除key?

等等
这些,在菜鸟教程里面都没具体描述。所以我写该篇文章,主要是描述在实际的生产中的使用。

lua的简单介绍还是要写一下:

EVAL script numkeys key [key …] arg [arg …]
首先大家一定要知道eval的语法格式,其中:
<1> script: 你的lua脚本
<2> numkeys: key的个数
<3> key: redis中各种数据结构的替代符号
<4> arg: 你的自定义参数
例子:
eval “return {KEYS[1],KEYS[2],KEYS[3],KEYS[4]}” 4 username age jack 20
类似于C#的占位符{0}

通常情况下,不会在redis-cli直接写lua脚本,一般都是放在lua文件中,方便编辑
redis提供了以下几个script命令,用于对于脚本子系统进行控制:
script flush:清除所有的脚本缓存
script load:将脚本装入脚本缓存,不立即运行并返回其校验和
script exists:根据指定脚本校验和,检查脚本是否存在于缓存
script kill:杀死当前正在运行的脚本(防止脚本运行缓存,占用内存)

话不多说,直接上代码(golang版本的代码)

func demo_01()  {
	redis_client := GetRedisConnect()

	/*result:=redis_client.ScriptLoad("return 'Hello GrassInWind'")
	fmt.Println(result.Val())*/
	// c66be1d9b54b3182f8d8e12f8b01a4e5c7c4af5b
	result:=redis_client.EvalSha("c66be1d9b54b3182f8d8e12f8b01a4e5c7c4af5b",nil)
	fmt.Println(result.Val())
}

func demo_02()  {
	redis_client := GetRedisConnect()
	/*result:=redis_client.ScriptLoad("return {KEYS[1],KEYS[2],KEYS[3],KEYS[4]}")
	fmt.Println(result.Val())*/
	//00bd8c0c171fada34c1c511fadf893e57bdb429e
	//4 username age jack 20
	result:=redis_client.EvalSha("00bd8c0c171fada34c1c511fadf893e57bdb429e",[]string{"username","jack","age","20"})
	fmt.Println(result.Val())
}

func demo_03()  {
	redis_client := GetRedisConnect()

	//设置分布式锁
/*	result:=redis_client.ScriptLoad("return redis.call('SETNX',KEYS[1],KEYS[2])")
	fmt.Println(result.Val())*/
	// ff1198b25bcb146e61488a7be91cc6df13ca40ce
	result:=redis_client.EvalSha("ff1198b25bcb146e61488a7be91cc6df13ca40ce",[]string{"OrderId202102231123","11000"})
	fmt.Println(result.Val())
}

//设置分布式锁+过期时间
func demo_04() {
	redis_client := GetRedisConnect()
	//设置分布式锁
	/*result := redis_client.ScriptLoad("return redis.call('SET',KEYS[1],KEYS[2],'ex',KEYS[3],'nx')")
	fmt.Println(result.Val())*/
	//9a391811e7f132f4e3b57e173e126a43f8e63700
	result := redis_client.EvalSha("9a391811e7f132f4e3b57e173e126a43f8e63700", []string{"OrderId202102231157", "22000","10000"})
	fmt.Println(result.Val())
}

关于里面的使用,再简单介绍下:

func demo_01()  {
	
	//获取redis客户端连接
	redis_client := GetRedisConnect()
	
	//把lua脚本运行在redis服务端
	/*result:=redis_client.ScriptLoad("return 'Hello GrassInWind'")
	fmt.Println(result.Val())*/

	//这是把lua脚本运行在redis服务端成功后返回的sha随机字符串编码
	// c66be1d9b54b3182f8d8e12f8b01a4e5c7c4af5b
	
	//此条lua脚本没有任务参数,执行的时候,只需要执行获取到的随机字符串编码即可得到结果
	result:=redis_client.EvalSha("c66be1d9b54b3182f8d8e12f8b01a4e5c7c4af5b",nil)
	fmt.Println(result.Val())
}
//设置分布式锁
func demo_04() {
	redis_client := GetRedisConnect()
	//设置分布式锁的lua脚本,
	/*result := redis_client.ScriptLoad("return redis.call('SET',KEYS[1],KEYS[2],'ex',KEYS[3],'nx')")
	fmt.Println(result.Val())*/
	//9a391811e7f132f4e3b57e173e126a43f8e63700
	result := redis_client.EvalSha("9a391811e7f132f4e3b57e173e126a43f8e63700", []string{"OrderId202102231157", "22000","10000"})
	fmt.Println(result.Val())
}
//批量设置分布式锁
func demo_05() {
	redis_client := GetRedisConnect()
	script := `local str=KEYS[1]
			 local delimiter=","
			 local result=""
			 local dLen = string.len(delimiter)
				local newDeli = ''
				for i=1,dLen,1 do
					newDeli = newDeli .. "["..string.sub(delimiter,i,i).."]"
				end
				local locaStart,locaEnd = string.find(str,newDeli)
				local arr = {}
				local n = 1
				while locaStart ~= nil
				do
					if locaStart>0 then
						arr[n] = string.sub(str,1,locaStart-1)
						n = n + 1
					end
					str = string.sub(str,locaEnd+1,string.len(str))
					locaStart,locaEnd = string.find(str,newDeli)
				end
				if str ~= nil then
					arr[n] = str
				end
				for k,v in pairs(arr) do
				local new_str=v
								 delimiter="-"
								 str=v
							  local dLen = string.len(delimiter)
							  local newDeli = ''
							  for i=1,dLen,1 do
									newDeli = newDeli .. "["..string.sub(delimiter,i,i).."]"
							  end
								local locaStart,locaEnd = string.find(str,newDeli)
								local arr = {}
								local n = 1
								while locaStart ~= nil
								do
									if locaStart>0 then
										arr[n] = string.sub(str,1,locaStart-1)
										n = n + 1
									end
									str = string.sub(str,locaEnd+1,string.len(str))
									locaStart,locaEnd = string.find(str,newDeli)
								end
								 if str ~= nil then
									 arr[n] = str
								 end
									local set_key=""
									local set_value=""
								for k, v in pairs(arr) do
								   if k==1 then
										print("这是键",k,v)
										 set_key=v
									else
										print("这是值",k,v)
										set_value=v
										print("set_key=",set_key,"set_value=",set_value)
										result=redis.call('SET',set_key,set_value,'ex','10000','nx')
									end
								 end
				end
				return result`
	result := redis_client.ScriptLoad(script)
	fmt.Println(result.Val())
	result1 := redis_client.EvalSha(result.Val(), []string{
		"php-2020,js-2010"})
	fmt.Println(result1.Val())
}

相信这个批量设置锁的功能,使用的比较多,但是大部分都是基于 redis命令实现的。并没有基于redis下的lua脚本实现的。本篇文章应该算是全网唯一有新颖的文章了吧!为了写这篇文章,花费了好几天的时间,因为在网上实在找不到现有的示例了。

//todo for循环计算值
func demo_06()  {
	redis_client := GetRedisConnect()
	
	script:=" local  Sum=0; for i=1,10 do   Sum=i+Sum end;return Sum "
	result :=redis_client.ScriptLoad(script)
	fmt.Println(result.Val())

	// 796b7c05ac15db5b8c11dc20a0e33e71dfcd973c
	result1 := redis_client.EvalSha(result.Val(),nil)
	fmt.Println(result1.Val())
}
//redis下lua函数
func demo_07()  {
	redis_client := GetRedisConnect()
	script:=`function add(a)
					print(a)
					return a
				end`
	result :=redis_client.ScriptLoad(script)
	fmt.Println(result.Val())
	result1 := redis_client.EvalSha(result.Val(),[]string{"1"})
	fmt.Println(result1.Val())
}

redis下的lua脚本是不支持函数的,但是把此代码拿去lua代码测试是能运行的,下面将详细讲一下为什么redis下的lua不支持函数。

//字符串拆分
func demo_08()  {
	redis_client := GetRedisConnect()

	/*script:=`local str=KEYS[1]
     		 local delimiter=","
			 local dLen = string.len(delimiter)
				local newDeli = ''
				for i=1,dLen,1 do
					newDeli = newDeli .. "["..string.sub(delimiter,i,i).."]"
				end
				local locaStart,locaEnd = string.find(str,newDeli)
				local arr = {}
				local n = 1
				while locaStart ~= nil
				do
					if locaStart>0 then
						arr[n] = string.sub(str,1,locaStart-1)
						n = n + 1
					end
					str = string.sub(str,locaEnd+1,string.len(str))
					locaStart,locaEnd = string.find(str,newDeli)
				end
				if str ~= nil then
					arr[n] = str
				end
				return arr`*/

	script:=`local str=KEYS[1] 
			 local split=function(str,delimiter)
				local dLen = string.len(delimiter)
				local newDeli = ''
				for i=1,dLen,1 do
					newDeli = newDeli .. "["..string.sub(delimiter,i,i).."]"
				end
			
				local locaStart,locaEnd = string.find(str,newDeli)
				local arr = {}
				local n = 1
				while locaStart ~= nil
				do
					if locaStart>0 then
						arr[n] = string.sub(str,1,locaStart-1)
						n = n + 1
					end
			
					str = string.sub(str,locaEnd+1,string.len(str))
					locaStart,locaEnd = string.find(str,newDeli)
				end
				if str ~= nil then
					arr[n] = str
				end
				return arr
			end   `

	result:=redis_client.ScriptLoad(script)

	result1:=redis_client.EvalSha(result.Val(),[]string{"php-2020,js-1010,go-2021"})

	fmt.Println(result1)

}

字符串拆分,一般都是配合redis命令使用,单独使用的不多。

在Redis中,运行脚本的命令有两个,分别为EVAL和EVALSHA,其中EVALSH是由EVAL衍生而来的。

  • EVAL

    示例:

EVAL "return 'hello world'" 0

EVAL 是执行整个脚本代码体。

  • EVALSHA
EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0    // 上一个脚本的校验和

而 EVALSHA 则要求输入某个脚本的 SHA1 校验和, 这个校验和所对应的脚本必须至少被 EVAL 执行过一次。或者曾经使用 SCRIPT LOAD 载入过这个脚本,因为 EVALSHA 是基于 EVAL 构建的。

EVAL命令分为两步:

1.为输入的脚本定义一个lua函数
2.执行这个lua函数

举个例子, 当执行命令 EVAL “return ‘hello world’” 0 时, Lua 会为脚本 “return ‘hello world’” 创建以下函数:

function f_5332031c6b470dc5a0dd9b4bf2030dea6d65de91()
    return 'hello world'
end

其中, 函数名以 f_ 为前缀, 后跟脚本的 SHA1 校验和(一个 40 个字符长的字符串)拼接而成。 而函数体(body)则是用户输入的脚本。
以函数为单位保存 Lua 脚本有以下好处:

  • 执行脚本的步骤非常简单,只要调用和脚本相对应的函数即可。
  • Lua 环境可以保持清洁,已有的脚本和新加入的脚本不会互相干扰,也可以将重置 Lua 环境和调用 Lua GC 的次数降到最低。
  • 如果某个脚本所对应的函数在 Lua 环境中被定义过至少一次,那么只要记得这个脚本的 SHA1 校验和,就可以直接执行该脚本 —— 这是实现 EVALSHA 命令的基础。

用简单的话说一下,通过ScriptLoad加载的lua脚本,会把lua的脚本加入到一个函数中,函数的名称是 字符串(SHA1 校验和(一个 40 个字符长的字符串)拼接而成的)。当在redis中,想再次执行这个函数,那通过EVALSHA 命令去执行。那也就是说,redis里的lua是不支持自定义函数的,只支持脚本命令,内置帮你定义函数,及时是在脚本里面定义闭包函数也是不支持的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值