设为首页 收藏本站
查看: 1074|回复: 0

[经验分享] Redis中使用Lua脚本的开发思路

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-3-31 08:25:21 | 显示全部楼层 |阅读模式
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<Boolean> scriptExists(String... sha1)
SCRIPT FLUSH
清除所有 Lua 脚本缓存
SCRIPT KILL杀死当前正在运行的 Lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效(如果已经执行了写操作,则需要通过shutdown nosave命令来处理)

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

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




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

  3.实际案例

   场景一:对一个特定请求1秒钟只允许访问10次,当符合请求访问条件时,返回True,否则返回False。
   Java客户端操作Redis服务,实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
     * 访问控制
     *
     * 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 = value <= 10;
            return rs;
        }
    }




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

  用Lua脚本来完成这一操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--[[
Judge status
KEYS[1]:key
ARGV[1]:request numbers
ARGV[2]:expires times seconds
--]]

local key, rqn, exp  = KEYS[1], ARGV[1], ARGV[2];
local value=redis.call("incr", key);
redis.log(redis.LOG_NOTICE, "incr "..key);
if(tonumber(value) == 1)then
   redis.call("expire", key,  exp);
   redis.log(redis.LOG_NOTICE, "expire "..key.." "..exp)
   return true;
else
   return tonumber(value) <= tonumber(rqn);
end




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

场景二:指定模式key批量删除
    redis目前提供的删除命令del仅支持删除指定数量的key,并不能通过指定模式key来进行删除,比如:del *user 删除以user结尾的key。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--[[
Pattern delete key
KEYS[1]:pattern
--]]

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

local keys=redis.call("keys", KEYS[1]);
local count = 0;
if(keys and (table.maxn(keys) > 0)) then
    for index, key in ipairs(keys) do
        redis.log(redis.LOG_NOTICE, "del "..key);
        count = count +  redis.call("del", key);
    end
end
return count;




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


场景三:生成随机数

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

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

  下面是实现随机数列表的Lua脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
--[[
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=1, ARGV[2], 1 do
    redis.call("lpush", KEYS[1], math.random());
end
redis.log(redis.LOG_NOTICE, "lpush " .. KEYS[1]);
return true;




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

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

wKioL1UYz4WSgIhaAAOqB1w19Sc305.jpg
wKiom1UYzkqx3NzwAAFpvCYWbRo501.jpg

wKiom1UYz6DwtZZyAAJG2oORc7c681.jpg

  对比上述结果,在执行该脚本时,随机数的生成由seed参数(第一个参数)决定的。
  相同随机数种子下生成的随机数是相同的,如果再次执行脚本,指定生成的随机数个数n小于已经生成的随机数个数m,则取已经生成的前n个,如果指定生成的随机数个数n大于已经生成的随机数个数m,则次数再生成(n-m)个随机数,并固定下来。

4.Redis中使用Lua脚本总结
    Redis内置了Lua解释器,这为操作Redis服务器和数据提供了巨大的灵活性。
    文中几个场景并不见得实际,有效,但并不能掩盖Lua与Redis结合将为Redis的使用提供了更大的想象和操作空间。
    我们可以通过Lua来实现更多特定功能的命令;用Lua来封装复杂了Redis操作的业务;计数,统计,分析,收集数据;实现业务操作事务控制等等。更多场景,还需在实际中不断摸索和尝试。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-52347-1-1.html 上篇帖子: redis 键值相关命令 下篇帖子: redis+keepalived高可用搭建 开发
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表