Redis Lua脚本好处、Redis执行Lua的两种方式、Redis缓存Lua脚本、Redis Lua原子性验证、Lua脚本IP限流、Lua脚本自乘
Redis Lua脚本好处、Redis执行Lua的两种方式、Redis缓存Lua脚本、Redis Lua原子性验证、Lua脚本IP限流、Lua脚本自乘
- Redis cli两种运行方式
- Redis Lua脚本好处
- Redis执行Lua的两种方式
- 1.交互式执行Lua
- Redis客户端执行Lua脚本命令
- Lua脚本中怎么执行Redis命令
- 2.命令式执行Lua
- Lua脚本文件注释编写
- Lua脚本文件编写
- 命令式执行简单Lua脚本文件
- Lua脚本案例
- 带参数的:Lua脚本实现IP限流
- 不带参数的:Lua脚本实现自乘
- Redis缓存Lua脚本
- Redis Lua原子性验证
Redis cli两种运行方式
redis cli两种运行方式:交互式、命令式
Redis Lua脚本好处
Lua脚本是Redis 2.6之后引入的
- 批量执行命令
- 原子性
- 操作集合的复用
Lua脚本实现原子性的原理,就是执行一个Lua脚本的时候,里面有多个命令,这个时候会阻塞其它任何命令的执行和其它任何客户端的请求,也就是说,执行Lua的时候,只会执行Lua里的命令,以此来达到Lua里的多条命令成为一个整体来执行。
Redis执行Lua的两种方式
一种是连接客户端,交互式执行Lua,一种是编写Lua脚本文件,用命令行的方式执行。
有很多坑,先了解一下
1、用交互式方式(redis-cli进入客户端再执行命令)执行eval命令的时候,后面不能跟lua脚本(只能跟Lua语言),会报错如下:
# test2.lua 0: test2.lua脚本路径 0参数个数
127.0.0.1:6379> eval test2.lua 0
(error) ERR Error compiling script (new function): user_script:1: '=' expected near '<eof>'
2、用命令式方式(redis-cli后面跟执行的命令)执行命令的时候,如果你的Redis设置了密码,在连接的时候会输出认证提示
# -r表示重复执行;这里连接认证之后执行3次ping命令
[pdx_haokai@VM-0-3-centos bin]$ redis-cli -a Pass9612 -r 3 ping
# 可以看到会有Warning: Using a password with '-a' or '-u'输出
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
PONG
PONG
PONG
这种输出会影响Lua脚本取值,但不影响正常运行
需要redis输入密码时去除提示Warning: Using a password提示,拼接如下命令可以去除提示
2>/dev/null
1.交互式执行Lua
Redis客户端执行Lua脚本命令
EVAL script numkeys [key [key ...]] [arg [arg ...]]
- EVAL代表执行Lua语言的命令
- script代表Lua脚本内容,内容引号引起来,建议用双引号
- numkeys表示参数中有多少个key,需要注意Redis中key是从1开始的,如果没有key的参数,写0
- [key [key …]]是key作为参数传递给Lua语言,也可以不填,但是需要[key [key …]]和numkeys的个数对应起来
- [arg [arg …]]这些参数传递给Lua语言,它们可填可不填
# "return '2234'"是Lua脚本内容,numkeys为0
127.0.0.1:6379> eval "return '2234'" 0
"2234"
127.0.0.1:6379>
Lua脚本中怎么执行Redis命令
Lua的使用,就是为了能够执行Redis命令
通过redis.call来执行Redis命令
redis.call(command,key[param1,param2,...])
- command是Redis命令,包括set、get、del等
- key是被操作的键
- key[param1,param2,…]代表给key的参数
# --eval:用于执行lua脚本
127.0.0.1:6379> eval "return redis.call('set','key001','567')" 0
OK
127.0.0.1:6379> get key001
"567"
# KEYS[1],ARGV[1]中KEYS和ARGV是形参名,大写不能改
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 key002 789
OK
127.0.0.1:6379> get key002
"789"
# KEYS[1],ARGV[1]中KEYS和ARGV是形参名,KEYS改为小写,直接不识别参数
127.0.0.1:6379> eval "return redis.call('set',keys[1],ARGV[1])" 1 key002 710
(error) ERR Error running script (call to f_a9e55ca6c2982f8ce741628e9c3df21db06f4edb): @enable_strict_lua:15: user_script:1: Script attempted to access unexisting global variable 'keys'
这里需要注意下,如果用交互式方式(redis-cli进入客户端再执行命令)执行eval命令的时候,后面不能跟lua脚本,是不识别的,会报错如下:
127.0.0.1:6379> eval test2.lua 0
(error) ERR Error compiling script (new function): user_script:1: '=' expected near '<eof>'
2.命令式执行Lua
Lua脚本文件注释编写
在Lua程序中,有两种注释,单行注释和多行注释
1.单行注释
单行注释可以注释整行或者一行中的一部分。他一般不用于连续多行的注释文本,当然,和其他语言一样,也可以用来注释掉多行连续的代码,例如:
-- 注释方式1
if x > 1 thenreturn true; -- 注释
elsereturn false; -- 注释
end-- 注释方式2
-- if x > 1 then-- return true;
--else-- return false;
-- end
在以上示例中,注释方式2是不规范的,如果要注释多行,尽量的采用多行注释。
2.多行注释
多行注释一般用于连续多行注释,当然也可以用于单行注释,例如:
--[[
if x > 1 then-- 注释1
else-- 注释2
end
--]]
Lua的多行注释是可以嵌套的,这是其他语言没有的优点。
Lua脚本文件编写
Lua语言中执行Redis命令,用起来不是很方便,所以都会使用去执行Lua脚本文件
# 编写操作Redis命令
redis.call(command,key[param1,param2,...])# 调用Lua脚本
redis-cli --eval 脚本名称 参数个数 参数1 参数2 ......
新建一个测试脚本:test.lua
vim test.lua
内容如下
--[[
多行注释:
这是一个测试脚本
--]]
redis.call('set','key1','123')--放入一个数字字符串
redis.call('incryby','key2','1024')--数字字符串增加1024
return redis.call('get','key2')
命令式执行简单Lua脚本文件
以命令式执行Lua脚本文件,交互式只不能执行Lua脚本文件(不识别文件的)。
redis-cli -a Pass9612 2>/dev/null --eval ../../lua/test.lua
- -a:如配置了密码,可用a选项,无密码可忽略
- 2>/dev/null:输入密码时去除提示Warning: Using a password提示,方便取值查看,无密码可忽略
- –eval 你的lua脚本文件路径
Lua脚本案例
案例会用到tonumber函数
tonumber函数会尝试将它的参数转换为数字
如果参数已经是一个数字或者是一个可以转换成数字的字符串,那么这个函数就会返回转换后的数值,否则,返回nil(表示转换失败)。
这个函数有一个额外的参数base可用来指定参数的进制:
(1)默认参数值是10
(2)参数的取值范围是[2, 36]
(3)当参数值超过10时,使用A代表10(大小写都可以),B代表11,以此类推最后Z代表35
带参数的:Lua脚本实现IP限流
每个用户在X秒内只能访问Y次
需要用到三个参数
- KEYS:key,IP限流,则key需要包含IP去区分
- ARGV[1]:第一个参数,X,用来设置过期时间
- ARGV[2]:第二个参数,Y,控制规定时间内访问次数
新建ip.lua文件,内容如下
--[[
1.Lua 中的变量全是全局变量,无论语句块或是函数里,除非用 local 显式声明为局部变量,变量默认值均为nil
2.使用local创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效。
(代码块:指的是一个控制结构内,一个函数体,或者一个chunk(变量被声明的那个文件或者文本串))尽可能使用局部变量,有两个好处
a.避免命名冲突
b.访问速度更快(原因是local变量是存放在lua的堆栈里面的是array操作,而全局变量是存放在_G中的table中,效率不及堆栈)
--]]
local num=redis.call('incr',KEYS[1])-- 计数+1,key不存在会新建
if tonumber(num)==1 then--第一次访问,用第一个参数设置过期时间redis.call('expire',KEYS[1],ARGV[1])return 1
elseif tonumber(num)>tonumber(ARGV[2]) then--不是第一次访问,跟第二个参数进行比较,是否超过限制return 0 --超过限制
elsereturn 1 --没有超过限制
end
命令方式执行Lua脚本ip.lua,10s中访问最多6次,appname_test1:ip:192.168.0.1是模拟的key
# 空格的写法需要注意(这是固定语法): key后面+空格+逗号+空格+参数1+空格+参数2
# 我redis有密码,用-a和2>/dev/null屏蔽密码提示
redis-cli -a Pass9612 2>/dev/null --eval ../../lua/ip.lua appname_test1:ip:192.168.0.1 , 10 6
不带参数的:Lua脚本实现自乘
Redis中有自增,但没有自乘。
传入key、arg1。对key乘以arg1,得到乘以的结果。
local curVal = redis.call('get',KEYS[1])
if curVal == false then--根据key获取当前value,没有key则为0curVal = 0
elsecurVal = tonumber(curVal)
end
curVal = curVal*tonumber(ARGV[1])
redis.call('set',KEYS[1],curVal)
return curVal
需要key有值,否则结果都是0
Redis缓存Lua脚本
eval "local curVal = redis.call('get',KEYS[1]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end; curVal = curVal*tonumber(ARGV[1]) ;redis.call('set',KEYS[1],curVal); return curVal" 1 num 2
以客户端执行乘法脚本为例,在redis客户端执行Lua语言,拼接了很多命令,如果这样的一长执行串命令发给服务端,那么网络通信就会产生比较大的开销,所以Redis支持在服务端直接缓存一部分脚本的内容(它是用脚本生成了一段摘要,服务端可以根据这个摘要去执行脚本内容)。
你可以把Lua脚本里的内容提取出来,转成一行,使用script load进行脚本内容的缓存,注意执行命令时单双引号,脚本中的’get’和命令load "local…"的单双引号不要重复,否则执行会报Invalid argument(s)
script load "local curVal = redis.call('get',KEYS[1]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end; curVal = curVal*tonumber(ARGV[1]) ;redis.call('set',KEYS[1],curVal); return curVal"
"5d134cb368cf22d88c9e0130cbae1775a22cf348"
需要key有值,否则结果都是0
Redis Lua原子性验证
Lua是保证原子性的,它的执行是排他的,Lua中的命令是一个整体,没执行完毕其它命令都将阻塞。
如果Lua脚本执行很慢,那么其它客户端执行命令都会陷入阻塞中,等待Lua脚本内容执行完毕。
客户端执行一个死循环Lua,不做任何值的修改
eval "while (true) do end" 0
其他客户端再执行命令,提示Redis正在忙于执行脚本,你只能通过SCRIPT KILL或者SHUTDOWN NOSAVE让这个脚本不再执行。
# 提示Redis正在忙于执行脚本,你只能通过SCRIPT KILL或者SHUTDOWN NOSAVE让这个脚本不再执行
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE
由于没有任何值的修改,因此可以直接SCRIPT KILL
客户端执行一个死循环Lua,涉及操作key
eval "redis.call('set','key3','333') while(true) do end" 0
可以看到SCRIPT KILL已经不可以了,因为你有对key进行操作
# 对不起,脚本已经对数据集执行了写命令。您可以等待脚本终止,或者使用SHUTDOWN NOSAVE命令强行终止服务器
UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.
SHUTDOWN NOSAVE是不保存当前的操作并强制Redis停机(正常停机是SHUTDOWN ),这种操作是由破坏性的,会导致一部分内容丢失。
SHUTDOWN NOSAVE之后,重新启动Redis,查看Lua中操作的key,并没有被保存
Redis Lua脚本好处、Redis执行Lua的两种方式、Redis缓存Lua脚本、Redis Lua原子性验证、Lua脚本IP限流、Lua脚本自乘相关推荐
- python3解释器执行not 1 and 1_编程语言的分类,python解释器多版本共存.执行python的两种方式,变量,用户与程序交互...
一.编程语言的分类? 机器语言:直接使用二进制指令编程,直接操作硬件,必须考虑硬件细节. 汇编语言:用简写的英文标识符取代二进制去编写程序,直接操作硬件,必须考虑硬件细节. 高级语言:通过人类能够理解 ...
- python脚本在linux上运行的两种方式_python脚本当作Linux中的服务启动实现方法
脚本服务化目的: python 在 文本处理中有着广泛的应用,为了满足文本数据的获取,会每天运行一些爬虫抓取数据.但是网上买的服务器会不定时进行维护,服务器会被重启.这样我们的爬虫服务就无法运行.这个 ...
- Python 执行代码的两种方式
1.交互执行即黑屏命令行执行 优点:即时调时程序,调试方便 缺点:无法永久无法保存代码 2.即文件存储代码执行Python代码文件 优点:可以永久保存代码 缺点:调试不方便 转载于:https://w ...
- linux执行jar的两种方式
在打包时一种指定了主类是哪个,一种没有指定,详见配置. 1.指定了的话直接使用下面命令执行: java -jar xxx.jar 参数 2.如果没有指定,则需要运行时手动指定: java -cp xx ...
- php操作redis_PHP操作redis的两种方式
随着redis使用越来越广泛,各种应用系统几乎都会嵌入redis.当然,PHP也不例外.在我接触到的项目中,主要是使用redis作为缓存服务器.但是对于PHP来说,它本身并不支持redis.所以说这里 ...
- Unity编辑器开发——通过模板创建Lua脚本的两种方式(二)
个人学习笔记,如有错误.疑问.建议,欢迎留言. 本文有关代码转载自:Unity3D 扩展编辑器实现创建Lua脚本 - 知乎 (zhihu.com) 声明:本文转载已取得原文章作者同意,有兴趣的可以关注 ...
- redis数据持久化到mysql_redis 数据持久化的几种方式
1.前言 Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为"半持久化模式"):也可以把每一次数据变化都写入到一个append only file( ...
- Redis持久化的两种方式
文章目录 前言 一.Redis持久化机制 二.RDB 三.AOF 总结 前言 Redis是基于内存的缓存机制,假定Redis服务器中途突然出现故障,那内存的数据就会丢失.针对这个问题,Redis提供了 ...
- Linux 开机自动执行脚本的两种方式
前言 很多情况下,我们都希望服务重启之后,很多应用都能自动启动,那么除了linux 提供的自启动配置之外,我们也可以在开机之后,通过指定 一些脚本的具体路径,或者是某个服务的启动命令具体路径,来进行服 ...
- sh执行文件 参数传递_详解shell中脚本参数传递的两种方式
方式一:$0,$1,$2.. 采用$0,$1,$2..等方式获取脚本命令行传入的参数,值得注意的是,$0获取到的是脚本路径以及脚本名,后面按顺序获取参数,当参数超过10个时(包括10个),需要使用${ ...
最新文章
- Python自学路线图之Python进阶
- 百度推出飓风算法,严厉打击恶劣采集
- python3 装饰器_python3装饰器
- linux可以生成pdb调试信息吗,Linux通过使用pdb简单调试python计划
- oracle db-link 分布式数据库网络配置协议错误,Oracle学习(18)【DBA向】:分布式数据库...
- android高德自定义图标,Android 高德地图显示在线图标
- c++ builder 中的 XMLDocument 类详解(2) - 记要
- 997西方行政学说 (2)
- 很多文章是在下转载贴在此处,是为了自己以后遇到类似问题一时想不起来
- python:linux中升级python版本
- Struts2中<s:iterator>基本用法及示例
- php.ini开启命名空间,Zend Framework教程之模型Model基本规则和使用方法
- tplink 2.4g弱信号剔除_科普 l 路由器信号2.4G和5G区别
- 浅谈css样式(border、background、table)
- Android 蓝牙开发(五)OPP接收文件
- 22年国内最牛的Java面试八股文合集(全彩版),不接受反驳
- 微型计算机控制技术王艳芳,基于单片机液位控制器的设计与实现最终版(样例3)...
- NLP在医学领域的应用(更新中)
- 19108期计算机开机号,排列三19108期藏机图诗汇总
- DM笔记之安装1:DM7 For NeoKylin A6
热门文章
- IDA安装lazyIDA
- 解密Cocos2D中的Lua源码
- 三角网导线平差实例_三角网间接平差示例
- MathType6.0安装教程
- 腾讯微博开放平台OAuth1.0授权完整流程(C#)
- 对于高级搜索部分的要求
- 数学建模写作指导20篇(一)-如何写好数学建模论文?
- Cisco(54)——STP理论(2)
- 必须安装三星系列android系统智能手机usb驱动程序,三星N9109W Android 5.0 (GALAXY Note 4 电信4G)usb驱动下载安装教程...
- IIS写入漏洞利用工具解析