首页 > 数据库 >lua脚本在redis中的使用

lua脚本在redis中的使用

时间:2023-03-04 20:35:33浏览次数:41  
标签:脚本 -- key1 redis lua call

  1. 先开启redis的日志输出

    修改redis.conf文件,设置logfile /root/tools/redis-6.0.9/logs/redis.log
    image-20230304130756593

  2. 重启redis
    systemctl restart redisd

  3. 创建一个简单的lua脚本test.lua

    --在redis日志文件中输入日志,并且日志级别是redis.LOG_NOTICE
    redis.log(redis.LOG_NOTICE,"测试打印控制台")
    
    return "123123123"
    
  4. 执行脚本

     ../redis-cli --eval test.lua
    

    返回值
    image-20230304131740897

​ 日志文件的控制台输入
image-20230304131808881

  1. 修改test.lua

    --相当于 set key1 value1
    redis.call("set","key1","value1")
    
    --相当于 get key1,然后把结果赋值给 var1
    local var1 = redis.call("get","key1")
    
    --返回var1
    return var1;
    

    执行结果
    image-20230304132425102
    查看redis中的 键多了一个名为key1的键
    image-20230304132533402

  2. redis.call 和 redis.pcall的区别

    • redis.call 如果遇到单挑命令错误,会中断整个脚本的执行,已经执行的不会回退

      --这句正常执行
      redis.call("set","key1","value2")
      --call  执行set1 这种非法的命令出错以后,直接抛出异常
      redis.call("set1","key1","value1")
      
      --相当于 get key1,然后把结果赋值给 var1
      local var1 = redis.call("get","key1")
      
      --返回var1
      return var1;
      

      正常返回:

      值已经被修改:

      image-20230304134804579

    • redis.pcall 如果遇到单条语句失败,会继续执行完整个脚本,已经执行的不会回退

      --这句正常执行
      redis.call("set","key1","value2")
      
      --pcall  执行set1 这种非法的命令出错以后,后面的命令依旧会执行(set1 是非法的命令)
      redis.pcall("set1","key1","value1")
      
      --相当于 get key1,然后把结果赋值给 var1
      local var1 = redis.call("get","key1")
      
      --返回var1
      return var1;
      

      返回结果:

      值已经被修改:
      image-20230304135901943

  3. lua 脚本的注释

    • 单行注释

      --这里是注释
      
    • 多行注释

      --[[
      
      		这里是多行注释
      
      		这里是注释的第二行
      
       ]]
      
  4. 参数的传入

    • 传入参数:redis-cli --eval 脚本名字 键1 键2 , 值1 值2 注意键和值中间有个逗号,键和值任意多个,并且可以不对称
      ../redis-cli --eval test.lua k1 k2 , v1 v2

    • 上面命令的键和值没有对应关系,可以把他们看出两种类型的参数,或者是会放到两个数组里面的参数,如果我们需要传入类似map这种,可以用参数KEYS 和 ARGV下标的对应关系老实现。

    • 使用参数,KEYS获取key的数组,ARGV获取值的数据,可以通过下标获取具体那个参数

      redis.log(redis.LOG_NOTICE,cjson.encode(KEYS))
      redis.log(redis.LOG_NOTICE,cjson.encode(ARGV))
      
      
      redis.log(redis.LOG_NOTICE,cjson.encode(KEYS[1]))
      redis.log(redis.LOG_NOTICE,cjson.encode(ARGV[1]))
      

      参数打印
      image-20230304143139817

  5. redis内置的lua库函数

    • redis.call 以会中断脚本执行的方式执行redis命令
    • redis.pcall 以不中断脚本执行的方式执行redis命名
    • cjson.encode 把lua的table装换成json字符串。
    • cjson.decode 把json字符串转成 lua脚本的 table 对象。
    • cmspack.pack 把table序列化成字符串
    • cmspack.unpack 把字符串还原成表
  6. 参数类型对应

    • redis的数字-->lua的数字

    • redis 的字符串-->lua的字符串

    • redis的多行字符串-->lua的表

    • redis执行状态返回值-->lua的表(ok,或者err)
      返回状态table是这个样子

      ​ {"ok":"OK"}
      ​ {"err":"@user_script: 4: Unknown Redis command called from Lua script"}
      如果直接 return table,返回的是value部分。
      image-20230304141748016

  7. 脚本加入缓存,然后执行

    • 需要先进入redis-cli才能执行,不能在linux的命令行执行,只能导入文本的脚本,不能是文件

    • 导入脚本
      script load "脚本内容"
      image-20230304145952165

    • 检查脚本是否存在
      script exists "30dc9ef2a8d563dced9a243ecd0cc449c2ea0144"

      image-20230304150243237

    • 执行脚本,需要指定 0 个参数
      evalsha "30dc9ef2a8d563dced9a243ecd0cc449c2ea0144" 0

    image-20230304150449548

    • 清空缓存的脚本
      script flush
  8. lua脚本长时间执行或者死循环问题

    • 当执行时间大于lua-time-limit以后,redis 会接受io请求,这时候在命令行执行脚本会有redis繁忙的提示。
    • lua-time-limit建议一定要设置,0或者负数表示没有限制。
    • 如果这时候正在运行的lua脚本没有执行过修改操作,那么可以通过 script kill 杀掉正常执行的脚本。
    • 如果已经有修改操作,redis为了保证原子性,是不让直接杀掉的。只能重启redis 服务,但是这样可能打破原子性。
  9. redis 的lua 脚本的原子性建立在单线程按序执行的基础上,并且要求redis server不能在无故重启。或者说redis lua脚本只是保证了按序执行,不被插队,并没有保证原子性,配合单线程执行命令,并且在不会突然重启的基础上,才能保证原子性。

  10. redis 的lua脚本中的操作没有隔离性。但是因为它是单线程执行命令的,别的线程进不来,没法读取到中间数据,所以表现的有隔离性。如果这时候被断电中断,就可以看到部分的中间数据。

  11. redis 里面的 lua 是在沙箱中运行的,不能使用全局变量,不能调用操作系统和文件等资源,只能对redis内部数据做修改。redis也对随机做了处理,2次脚本执行,脚本里面的第N次获取的随机数总是一样的。需要要不一样,需要通过随机数种子来实现。

  12. 在java程序中使用lua脚本
    配置按照string类型序列化redisTemplate

        /**
         *
         * redisTemplateString 用于处理字符串格式,或者不带类型的json字符串
         *
         * @param redisConnectionFactory
         * @return
         */
        @Bean
        public RedisTemplate<String, String> redisTemplateString(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
    
            
            //相当于key,value都使用string类型序列化
            redisTemplate.setDefaultSerializer( new StringRedisSerializer() );
            return redisTemplate;
        }
    
    

    lua脚本

    --相当于 set key1 value1
    redis.call("set", KEYS[1] , ARGV[1] );
    redis.call("set", KEYS[2] , ARGV[2] );
    
    --相当于 get key1,然后把结果赋值给 var1
    local value1 = redis.call("get",KEYS[1]);
    local value2 = redis.call("get",KEYS[2]);
    
    local data = {};
    data.data1 = value1;
    data.data2 = value2;
    
    --返回json字符串
    return cjson.encode( data );
    
    --如果java那边用的带有类型的json需要在返回值两边加上字符串标记。
    --return "\"" .. "asdasdasd" .. "\"";
    

    使用java程序调用lua脚本

    	@Resource
    	RedisTemplate<String, String> redisTemplateString;
    
    	@ApiOperation(value = "调用lua脚本")
    	@RequestMapping(value="lua/t1", method= {RequestMethod.GET})
    	public Object t2() throws Exception{
    		DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
    		redisScript.setResultType(String.class);
    
    		redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/test.lua")));
    		//RedisScript<Map> redisScript = RedisScript.of(new ClassPathResource("lua\\test.lua"));
    
    		List<String> keys = new ArrayList<>();
    
    
    		keys.add("k1");
    		keys.add("k2");
    
    
    		Object rt = redisTemplateString.execute(redisScript, keys, "v1","v2");
    		System.out.println( rt.getClass() );
    
    		return rt;
    	}
    

    需要注意的是如果是带有java类型的json序列化方式和 不带类型的json序列化方式不兼容的。

    如果是返回string类型并且加了"内容",是可以兼容的。

    lua脚本返回值使用的是value类型的序列化方式。

标签:脚本,--,key1,redis,lua,call
From: https://www.cnblogs.com/cxygg/p/17179019.html

相关文章

  • lua脚本的使用
    先开启redis的日志输出修改redis.conf文件,设置logfile/root/tools/redis-6.0.9/logs/redis.log重启redissystemctlrestartredisd创建一个简单的lua脚本test.......
  • Lua 学习---2
    值和类型 Luaisa dynamicallytypedlanguage.Thismeansthatvariablesdonothavetypes;onlyvaluesdo.Therearenotypedefinitionsinthelanguage.A......
  • 【Redis场景5】集群秒杀优化-分布式锁
    集群环境下的秒杀问题前序【Redis场景1】用户登录注册【Redis场景2】缓存更新策略(双写一致)【Redis场景3】缓存穿透、击穿问题【Redis场景拓展】秒杀问题-全局唯一ID......
  • 定位解析一个因脚本劫持导致webpack动态加载异常的问题
    问题描述项目现场的前端项目在点击顶部的导航栏切换不同的模块时,会有小概率出现模块加载报错的情况:我们的前端项目里是有基于react-loadable做的懒加载的,上图的12.be789......
  • 学习Lua--1
    1、标识符Names (alsocalled identifiers)inLuacanbeanystringofletters,digits,andunderscores,notbeginningwithadigit. Identifiersareusedto......
  • Shell脚本练习
    企业面试题京东问题1:使用Linux命令查询file1中空行所在的行号。[root@server~]#catfile1问题1:使用Linux命令查询file1中空行所在的行号。[root@server~]#awk'/^......
  • 【mysql】Linux下定时备份数据shell脚本
    mysql_full_bak.sh#!/bin/bash#全量备份(建议一周一次)#时间参数DATE=$(date+%Y-%m-%d)DATETIME=$(date+%Y%m%d%H%M%S)#数据库的地址HOST=localhost#数据库的......
  • redis cluster 部署
    rediscluster部署服务器说明192.168.2.200:7000...192.168.2.200:7005创建集群目录mkdircluster-testcdcluster-testmkdir7000700170027003700470......
  • Windows系统安装Redis服务
    网上有很多的安装教程,但Redis版本都比较老,最近从github上搜索到最新版本,验证可用,记录一下1、应用下载地址:https://github.com/zkteco-home/redis-windows2、选择对应得ta......
  • 干货!超实用的 Linux 初始化脚本
    咸鱼今天给大家分享一个无论是学习还是工作中都很实用的Linux系统初始化脚本,其实就是各种命令的集合 完整代码在文章最后哦 定义相关变量   配置yum镜像......