用jedis执行lua脚本

摘要:
Redis2.6版本通过内嵌支持Lua环境。下表列出了redis脚本常用命令:序号命令及描述1EVALscriptnumkeyskey[key...]arg[arg...]执行Lua脚本。5SCRIPTKILL杀死当前正在运行的Lua脚本。6SCRIPTLOADscript将脚本script添加到脚本缓存中,但并不立即执行这个脚本。jedis.evalsha()根据sha1编码和keyvalue值执行脚本,返回结果。

1.Redis 脚本

Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为EVAL。

下表列出了 redis 脚本常用命令:

序号命令及描述
1EVAL script numkeys key [key ...] arg [arg ...]
执行 Lua 脚本。
2EVALSHA sha1 numkeys key [key ...] arg [arg ...]
执行 Lua 脚本。
3SCRIPT EXISTS script [script ...]
查看指定的脚本是否已经被保存在缓存当中。
4SCRIPT FLUSH
从脚本缓存中移除所有脚本。
5SCRIPT KILL
杀死当前正在运行的 Lua 脚本。
6SCRIPT LOAD script
将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。

2.lua 文件编写和执行

ip_limit.lua

--IP限流,对某个IP频率进行限制 ,1分钟访问10次
local num=redis.call('incr',KEYS[1])
if tonumber(num)==1then
    redis.call('expire',KEYS[1],ARGV[1])
    return 1elseif tonumber(num)>tonumber(ARGV[2]) then
    return 0
else 
    return 1end

执行ip_limit.lua脚本:

./redis-cli --eval  "ip_limit.lua"  app:ip:limit:192.168.1.15 , 6000 10(integer) 1

注意
1) app:ip:limit:192.168.1.15 是key值 ,后面是参数值,中间要加上一个空格 和 一个逗号,再加上一个 空格 。即:./redis-cli –eval [lua脚本] [key…]空格,空格[args…]
2) 多个参数之间用一个 空格 分割 。

3.evalsha 的基本使用

每次使用 eval 执行很长的脚本其实没什么必要, redis可以将脚本缓存起来,生成一个脚本的 SHA1 ,标记这个这个脚本,然后 使用时传 SHA1 和key value 的值即可。

92:0>script load "return redis.call('set',KEYS[1],ARGV[1])"# 这段脚本 生成了SHA1 值,来标记其唯一性
"c686f316aaf1eb01d5a4de1b0b63cd233010e63d"                                     
92:0>evalsha c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1AA BB   # 使用 evalsha 命令 和 SHA1 值来执行脚本
"OK"
92:0>get AA          # 验证数据存储是否成功
"BB"

4.redis 和 java 整合

jedis.scriptLoad方法将script脚本添加到脚本缓存中,如果脚本没有加载过,那么进行加载,这样就会返回一个sha1编码。
jedis.evalsha() 根据sha1编码和 key value值执行脚本,返回结果。

依赖:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>

代码如下,

Jedis连接池:

public classJedisPoolUtils {

    private staticJedisPool jedisPool;

    public staticJedisPool getInstance() {
        if (jedisPool == null) {
            synchronized (JedisPoolUtils.class) {
                if (jedisPool == null) {
                    JedisPoolConfig jedisPoolConfig = newJedisPoolConfig();
                    jedisPoolConfig.setMaxTotal(20);
                    jedisPoolConfig.setMaxIdle(10);
                    jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 4000, "123456");
                }
            }
        }
        returnjedisPool;
    }
}

测试实现类:

public classLuaDemo{

    @Test
    public voidtestScriptLoad() {
        JedisPool jedisPool =JedisPoolUtils.getInstance();
        Jedis jedis =jedisPool.getResource();
        System.out.println(jedis);
         String lua = "local num = redis.call('incr', KEYS[1])
" +
                 "if tonumber(num) == 1 then
" +
                 "	redis.call('expire', KEYS[1], ARGV[1])
" +
                 "	return 1
" +
                 "elseif tonumber(num) > tonumber(ARGV[2]) then
" +
                 "	return 0
" +
                 "else 
" +
                 "	return 1
" +
                 "end
";
        String scriptLoad=jedis.scriptLoad(lua);
        System.out.println(scriptLoad);
    }    
    
    @Test
    public  voidtestEvalsha() {
        JedisPool jedisPool =JedisPoolUtils.getInstance();
        Jedis jedis =jedisPool.getResource();
        try{
           String scriptLoad ="5ae1f63a19ef16ea0d8c0268d01c5fa01312b7e6";  //来自上面的 testScriptLoad()的值
            Object result = jedis.evalsha(scriptLoad , Arrays.asList("localhost"), Arrays.asList("10000", "2"));
            System.out.println("aaa:"+result);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(jedis != null){
                try{
                    jedis.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    @Test
    public void script() throwsInterruptedException {
    
        JedisPool jedisPool =JedisPoolUtils.getInstance();
        Jedis jedis =jedisPool.getResource();
        List<String> keys = new ArrayList<>();
        List<String> vals = new ArrayList<>();
        
        //测试1、 基本测试
        Object eval = jedis.eval("return 1", keys, vals);
        System.out.println(eval);
        
        //测试2、 eval  里面也可以是一个文件
        keys.add("kk");
        Object eval2 = jedis.eval("local tab={}  for i=1,#KEYS do  tab[i] = redis.call('get',KEYS[i]) end return tab",
                keys, vals);
        System.out.println(eval2);
        
        //测试3、 scriptLoad 
        //好处:这样可以缓存到服务器,不用每次把lua脚本的内容传过去
        String lua = "local tab={}  for i=1,#KEYS do  tab[i] = redis.call('get',KEYS[i]) end return tab";
        String scriptLoad =jedis.scriptLoad(lua);
        System.out.println(scriptLoad);
        Object evalsha =jedis.evalsha(scriptLoad, keys, vals);
        System.out.println(evalsha);
   }       
}

参考:

https://zixuephp.net/manual-redis-2328.html

https://blog.csdn.net/xiaojin21cen/article/details/88621540

https://blog.csdn.net/weixin_38070406/article/details/78246786

免责声明:文章转载自《用jedis执行lua脚本》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇让Apache Shiro保护你的应用基于sqlite的Qt 数据库封装下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

mac mamp pro 安装swoole和redis 拓展

方法1 官方文档介绍直接打开mamp pro 点击redis 打勾include redis server in groupStart即可 phpredis拓展自动就能够用了。 我目前用的是第二种方法, redis 客户端 https://gitee.com/qishibo/AnotherRedisDesktopManager 我现在用的是这个。 打开re...

ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十五节--缓存小结与ABP框架项目中 Redis Cache的实现

返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 缓存 为什么要用缓存 为什么要用缓存呢,说缓存之前先说使用缓存的优点。 减少寄宿服务器的往返调用(round-trips)。 如果缓存在客户端或是代理,将减少对服务器的请求,减少带宽。 减少对数据库服务器的往返调用(round-trips)。 当内容缓存在web服务器,...

redis慢查询的简单认识和事务、订阅的认识

  有学习的小伙伴可以一起讨论有问题可以问我,微信 15321502296慢查询 阀值:规定的一个值 阈值:规定的一个范围 就是系统在执行命令前后计算每条命令的执行时间,当超过预设的阀值时,就见这条命令记录下来 slowlog-log-slower-than  微秒 1秒=1000毫秒=1000000微秒 0 记录所有命令 <0不进行记录 slowl...

Redis——集群(cluster)

前言 在前面的文章中,已经介绍了Redis的几种高可用技术:持久化、主从复制和哨兵,但这些方案仍有不足,其中最主要的问题是存储能力受单机限制,以及无法实现写操作的负载均衡。 Redis集群解决了上述问题,实现了较为完善的高可用方案。本文将详细介绍集群。 主要内容包括:集群的作用;集群的搭建方法及设计方案;集群的基本原理;客户端访问集群的方法;以及其他实践中...

自定义注解和使用

1、使用@interface关键定义注解(RateLimiter.java),如下: package com.vx.servicehi.annotation; import java.lang.annotation.*; /** * @author wangbs * @version 1.0 * @date 2019/12/16 1:25 *...

Bootstrap Multiselect插件使用步骤以及常见参数配置介绍

    Multiselect是基于jQuery插件的,它可以以下拉列表的形式为用户提供选择内容,能进行单选或者多选。它应用的主要步骤如下: 一,引入需要的相关js和css文件 既然是Bootstrap插件,又是基于jQuery的,很明显主要就导入这两个类型的文件 <link rel="stylesheet" href="http://t.zouka...