Redis中使用Lua脚本的开发思路

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,经济版 1GB 1个月
简介:

 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操作的业务;计数,统计,分析,收集数据;实现业务操作事务控制等等。更多场景,还需在实际中不断摸索和尝试。



本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1626372,如需转载请自行联系原作者

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
17天前
|
消息中间件 NoSQL Java
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
46 0
|
17天前
|
存储 缓存 NoSQL
Redis系列学习文章分享---第十三篇(Redis多级缓存--JVM进程缓存+Lua语法)
Redis系列学习文章分享---第十三篇(Redis多级缓存--JVM进程缓存+Lua语法)
32 1
|
16天前
|
NoSQL API Redis
使用Redis Lua脚本实现高级限流策略
使用Redis Lua脚本实现高级限流策略
33 0
|
17天前
|
消息中间件 NoSQL Java
Spring Boot中使用Redis和Lua脚本实现延时队列
Spring Boot中使用Redis和Lua脚本实现延时队列
|
缓存 NoSQL Redis
Redis命令——脚本
Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。
1367 0
|
缓存 NoSQL Redis
redis必杀命令:脚本
题记: Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。
1266 0
|
10天前
|
NoSQL Java Redis
软件开发常见流程之宝塔初始化安装环境配置,Lam前面不选,直接跳商城,在宝塔内点击软件商城,安Mysql5.7,安java项目管理器,安Ngnix最新版,安Redis
软件开发常见流程之宝塔初始化安装环境配置,Lam前面不选,直接跳商城,在宝塔内点击软件商城,安Mysql5.7,安java项目管理器,安Ngnix最新版,安Redis
|
12天前
|
存储 消息中间件 缓存
apt安装Redis 7
【7月更文挑战第2天】
|
17天前
|
存储 NoSQL 关系型数据库
Redis系列学习文章分享---第一篇(Redis快速入门之初始Redis--NoSql+安装redis+客户端+常用命令)
Redis系列学习文章分享---第一篇(Redis快速入门之初始Redis--NoSql+安装redis+客户端+常用命令)
42 1
|
27天前
|
NoSQL Redis Windows
win10下Redis安装、启动教程
win10下Redis安装、启动教程
22 2