redis执行lua脚本

redis执行lua脚本

文档

  1. redis单机安装
  2. redis常用的五种数据类型
  3. springboot整合redis-RedisTemplate单机模式
  4. 布隆过滤器 -Bloom Filter

官方文档

  1. 官网操作命令指南页面:https://redis.io/docs/latest/commands/?name=get&group=string
  2. Redis cluster specification
  3. Distributed Locks with Redis

说明

  1. redis版本:7.0.0
  2. springboot版本:3.2.0

redis执行lua脚本

安装单机版redis
  1. 安装单机版redis参考文档:redis单机安装
编写lua脚本
  1. 在 Redis 中原子地尝试给 key 设值并设置过期时间,成功返回 1,失败(key 已存在)返回 0,用作分布式加锁。

  2. 编写一个lua脚本,为指定的 key 设置一个值并同时设置毫秒级过期时间,成功则返回 1,失败则返回 0。

  3. 脚本示例

    javascript 复制代码
    -- KEYS[1]  : 锁的 key
    -- ARGV[1]  : 锁的唯一标识
    -- ARGV[2]  : 过期时间(毫秒)
    -- 第一步:只有在 key 不存在时才设置值,相当于 SET key value NX
    
    if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then
      -- 第二步:设置过期时间,相当于 PX ttl
      redis.call('PEXPIRE', KEYS[1], ARGV[2])
      return 1       -- 加锁成功
    else
      return 0       -- 加锁失败(已有锁)
    end
  4. 简化成一行,保存为脚本文件tryLock.lua

    javascript 复制代码
    if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end
  5. 什么是KEYSARGV

    1. KEYS是一个数组,存放"所有 key 名"
    2. ARGV是一个数组,存放"所有非 key 的参数"
  6. 为什么区分KEYSARGV

    1. 技术上"都用 ARGV"在单机上多数能跑,但违反规范且在集群/路由/工具支持上会出问题
    2. Redis 之所以设计区分 KEYSARGV,就是为了让服务器明确知道哪些参数是 key,用于槽位路由、读写保护和各种内部/客户端工具对 key 的正确识别与处理。
lua脚本常见用法
  1. 常见用法是使用eval命令,传入lua脚本字符及参数,格式如下

    shell 复制代码
    EVAL script numkeys key [key ...] arg [arg ...]
    • script:Lua 脚本内容(字符串)
    • numkeys:后面有多少个 key
    • key [key ...]:传给脚本的 key 列表(用 KEYS[i] 访问)
    • arg [arg ...]:传给脚本的参数列表(用 ARGV[i] 访问)
  2. 示例,执行lua脚本

    shell 复制代码
    127.0.0.1:6379> EVAL "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end" 1 myLockKey lockValue 30000
    (integer) 1
    • 1:有 1 个 key
    • myLockKeyKEYS[1]
    • lockValueARGV[1]
    • 30000ARGV[2](过期时间,毫秒)
加载lua脚本到内存
  1. 将lua脚本加载到内存中

    shell 复制代码
    127.0.0.1:6379> SCRIPT LOAD "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end"
    "9aab773b2f1a82e88fcb760f3c42bbd9b9d3b1c3"
    • 返回值是一个 SHA1,使用EVALSHA 命令时会用到
    • 用同一段脚本内容再执行一次SCRIPT LOAD,会得到同一个 SHA
  2. 把 Lua 脚本加载到内存的作用

    核心作用:为了后面可以用 EVALSHA 复用脚本,减少网络传输和解析开销。

    • 性能更好:
      • 第一次用 SCRIPT LOAD 把脚本送到 Redis,服务器记住脚本内容和它的 SHA1。
      • 后面每次只发一个短短的 SHA1(EVALSHA ...),不用再把整段 Lua 文本发过去,也不用每次重新解析脚本。
    • 避免重复发送脚本:
      • 对于固定的业务脚本(比如分布式锁、库存扣减),脚本内容都是不变的,加载一次后就可以被很多次复用。
    • 方便客户端封装:
      • 客户端可以在启动时统一 SCRIPT LOAD 一批脚本,以 SHA1 为 ID 管理它们,调用时只用 EVALSHA 即可。
  3. 在shell中,可以通过读取lua脚本文件内容的方式将lua脚本加载到内存

    shell 复制代码
    cd /opt/module/redis/bin
    ./redis-cli -a 123456 SCRIPT LOAD "$(cat ../lua-data/tryLock.lua)"
    • 这种方式只能在 shell 中用 redis-cli 执行,不能在 redis-cli 里直接执行
检查lua脚本是否已加载
  1. 检查lua脚本是否已加载,实际上是检查lua脚本加载后返回的 SHA1是否存在

  2. 命令示例

    shell 复制代码
    127.0.0.1:6379> SCRIPT EXISTS 9aab773b2f1a82e88fcb760f3c42bbd9b9d3b1c3
    1) (integer) 1
    • 如果返回 1,表示已加载
    • 如果返回 0,表示这个 SHA 对应的脚本当前没在内存中
执行lua脚本
  1. 脚本先加载到内存:SCRIPT LOAD ,得到SHA值,用EVALSHA执行这个SHA值

    shell 复制代码
    127.0.0.1:6379> EVALSHA 9aab773b2f1a82e88fcb760f3c42bbd9b9d3b1c3 1 myLockKey lockValue 30000
    (integer) 1
  2. 直接执行脚本字符串:EVAL,这也是常见的用法

    shell 复制代码
    127.0.0.1:6379> EVAL "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end" 1 myLockKey lockValue 30000
    (integer) 1
  3. 在shell中,通过脚本文件执行:redis-cli --eval tryLock.lua

    shell 复制代码
    ./redis-cli -a 123456 --eval ../lua-data/tryLock.lua myLockKey , lockValue 30000
    • 逗号前是 KEYS,逗号后是 ARGV

参考资料

  1. https://www.bilibili.com/video/BV13R4y1v7sP

注意事项

  1. 部分内容由AI生成
  2. 如有不对,欢迎指正!!!
相关推荐
indexsunny2 小时前
互联网大厂Java面试实战:微服务与Spring Boot在电商场景下的应用解析
java·spring boot·redis·docker·微服务·kubernetes·oauth2
Andytoms2 小时前
错误信息:请在mysql配置文件修sql-mode或sql_mode为NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
数据库·sql·mysql
ChaITSimpleLove2 小时前
PostgreSQL(简称 pgsql)数据库的启动与关闭
数据库·postgresql·start·stop·reload·restart
数据知道2 小时前
MongoDB的Kerberos认证:详细讲述大型企业环境安全集成的实用技术
数据库·安全·mongodb
仙俊红2 小时前
项目上线后,发现一个接口比较慢,应该如何排查
数据库
yuezhilangniao2 小时前
centos7 yum安装PostgreSQL 15 与运维指南
数据库·postgresql
222you2 小时前
Mysql的索引以及底层的数据结构(面试)
数据结构·数据库·mysql
A10169330712 小时前
Nginx与frp结合实现局域网和公网的双重https服务
数据库·nginx·https