热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javaredis执行lua_Redis中使用Lua脚本的开发思路

Redis提供了通过eval命令来执行Lua脚本。下面通过几个小例子来讲述如何在Redis服务端执行Lua脚本。1.执行Lua脚本的几个命令如下:命令格式说明对应Je

Redis提供了通过eval命令来执行Lua脚本。下面通过几个小例子来讲述如何在Redis服务端执行Lua脚本。

1. 执行Lua脚本的几个命令如下:

命令格式说明对应Jedis客户端Jedis对象的方法之一(有更多重载方法)

EVAL script numkeys key [key ...] arg [arg ...]执行Lua脚本public Object eval(String script, int keyCount, String... params)

EVALSHA sha1 numkeys key [key ...] arg [arg ...]根据给定的 sha1 校验码,对缓存在服务器中的脚本进行求值public Object evalsha(String sha1, int keyCount, String... params)

SCRIPT LOAD script将给定的脚本缓存,不执行,并返回sha1校验值public String scriptLoad(String script)

SCRIPT EXISTS sha1 [sha1 ...]给定一个或多个脚本的 SHA1 校验和,返回一个包含 0 和 1 的列表,表示校验和所指定的脚本是否已经被保存在缓存当中public List scriptExists(String... sha1)

SCRIPT FLUSH清除所有 Lua 脚本缓存

SCRIPT KILL杀死当前正在运行的 Lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效(如果已经执行了写操作,则需要通过shutdown nosave命令来处理)

2.通过redis-cli客户端执行Lua脚本

redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3

需要注意的是用逗号来分割key和参数,这里与在交互式模式下执行evel命令有所不同。

3.实际案例

场景一:对一个特定请求1秒钟只允许访问10次,当符合请求访问条件时,返回True,否则返回False。

Java客户端操作Redis服务,实现代码如下:

/**

* 访问控制

*

* 1秒内最多可访问10次

*

* @param key

* @return

*/

public boolean isAccess(String key) {

String rkey = "acc:" + key;

long value = jedis.incr(rkey);

if (value == 1) {

jedis.expire(rkey, 1);

return true;

} else {

boolean rs &#61; value <&#61; 10;

return rs;

}

}

INCR命令作为计数器&#xff0c;如果rkey存在&#xff0c;则增加1返回最终值&#xff0c;否则初始化值为0&#xff0c;然后加1。如上程序&#xff0c;如果访问rkey不存在&#xff0c;则表示第一次请求&#xff0c;这时对其rkey设置过期时间为1秒&#xff0c;否则比较其值是否超过制定请求数的阀值10.

用Lua脚本来完成这一操作&#xff1a;

--[[

Judge status

KEYS[1]:key

ARGV[1]:request numbers

ARGV[2]:expires times seconds

--]]

local key, rqn, exp  &#61; KEYS[1], ARGV[1], ARGV[2];

local value&#61;redis.call("incr", key);

redis.log(redis.LOG_NOTICE, "incr "..key);

if(tonumber(value) &#61;&#61; 1)then

redis.call("expire", key,  exp);

redis.log(redis.LOG_NOTICE, "expire "..key.." "..exp)

return true;

else

return tonumber(value) <&#61; tonumber(rqn);

end

通过Java客户端代码实现该功能存在一定缺陷&#xff0c;比如每1秒就需要操作1个incr和expire命令&#xff0c;并且该命令是由客户端通过网络发起的&#xff0c;而使用Lua脚本则既可以保证操作的原子性&#xff0c;又能使每次操作只需要一个key即可在服务器端完成相应的判断操作。可以通过SCRIPT LOAD的方式将脚本缓存到服务器&#xff0c;通过sha1校验值&#43;参数(Key&#xff0c;ARG)来执行&#xff0c;减轻网络传输&#xff0c;也对该功能做到较好的封装。

场景二&#xff1a;指定模式key批量删除

redis目前提供的删除命令del仅支持删除指定数量的key,并不能通过指定模式key来进行删除&#xff0c;比如&#xff1a;del *user 删除以user结尾的key。

在redis中提供了keys命令&#xff0c;该命令可以通过指定模式key来获取key列表&#xff0c;下面通过keys和del命令组合实现一个指定模式key批量删除的命令。

--[[

Pattern delete key

KEYS[1]:pattern

--]]

redis.log(redis.LOG_NOTICE, "call keys "..KEYS[1]);

local keys&#61;redis.call("keys", KEYS[1]);

local count &#61; 0;

if(keys and (table.maxn(keys) > 0)) then

for index, key in ipairs(keys) do

redis.log(redis.LOG_NOTICE, "del "..key);

count &#61; count &#43;  redis.call("del", key);

end

end

return count;

需要注意的是场景二可以作为一种思路&#xff0c;通过Lua脚本组合redis内置命令来实现特定功能的命令。而这里的模式key批量删除并未一个好的命令&#xff0c;因为如果key的数量很大时&#xff0c;将会有比较严重的性能问题。redis默认限制Lua脚本执行时间最大为5秒&#xff0c;如果超过5秒将继续接受来自客户端的请求&#xff0c;并简单的返回BUSY结果。这时候则需要SCRIPT KILL或者SHUTDOWN NOSAVE命令做相应的处理。因此应该尽力保证脚本的执行速度极快。

场景三&#xff1a;生成随机数

对于Redis而且&#xff0c;脚本执行在相同数据集&#xff0c;相同参数下执行写命令具有一致性的。其不依赖与隐式的数据集&#xff0c;脚本执行过程中不同执行时期的状态变化&#xff0c;也不依赖外部I/O设备的输入。

要符合Redis服务执行的脚本条件&#xff0c;需要注意的地方比较多&#xff0c;可以参见&#xff1a;                         http://redis.io/commands/eval

下面是实现随机数列表的Lua脚本&#xff1a;

--[[

Random lpush a list key-value

KEYS[1]:key name

ARGV[1]:ramdom seed value

ARGV[2]:add element count

--]]

math.randomseed(ARGV[1]);

for i&#61;1, ARGV[2], 1 do

redis.call("lpush", KEYS[1], math.random());

end

redis.log(redis.LOG_NOTICE, "lpush " .. KEYS[1]);

return true;

上述脚本通过改变randomseed函数的参数来实现随机数&#xff0c;如果两次执行上述脚本&#xff0c;ARGV[1]参数值相同&#xff0c;则产生的随机数是相同的。

通过执行上述脚本&#xff0c;记录每次生产的值&#xff0c;然后删除对应key,再次生成。

4bfc061c720a5aa03d7c4b69343dce6a.png

d8fe7f92f4d11b9a0c7a1439e7a2fa12.png

27a5262519522fa20a70f5533716eeb9.png

对比上述结果&#xff0c;在执行该脚本时&#xff0c;随机数的生成由seed参数(第一个参数)决定的。

相同随机数种子下生成的随机数是相同的&#xff0c;如果再次执行脚本&#xff0c;指定生成的随机数个数n小于已经生成的随机数个数m&#xff0c;则取已经生成的前n个&#xff0c;如果指定生成的随机数个数n大于已经生成的随机数个数m,则次数再生成(n-m)个随机数&#xff0c;并固定下来。

4.Redis中使用Lua脚本总结

Redis内置了Lua解释器&#xff0c;这为操作Redis服务器和数据提供了巨大的灵活性。

文中几个场景并不见得实际&#xff0c;有效&#xff0c;但并不能掩盖Lua与Redis结合将为Redis的使用提供了更大的想象和操作空间。

我们可以通过Lua来实现更多特定功能的命令&#xff1b;用Lua来封装复杂了Redis操作的业务&#xff1b;计数&#xff0c;统计&#xff0c;分析&#xff0c;收集数据&#xff1b;实现业务操作事务控制等等。更多场景&#xff0c;还需在实际中不断摸索和尝试。



推荐阅读
author-avatar
超人懒懒_673
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有