在redis使用lua的一些情况记录

本文介绍在Lua脚本中如何通过HMGET命令批量获取Redis的Hash数据,以减少网络往返次数,显著提升效率。同时,探讨了在Lua中切换Redis数据库的方法及安全性问题。
  1. 在lua里面获取多次redis的hash数据,
    local a = redis.call('hget','test','a');
    local b = redis.call('hget','test','b');
    local c = redis.call('hget','test','c');
    local d = redis.call('hget','test','d');

    可以写成如下代码,减少访问redis的次数,提高效率

    local testlist= redis.call('HMGET','test','a','b','c','d');
    local a = testlist[1];
    local b = testlist[2];
    local c = testlist[3];
    local d = testlist[4];
    

    测试之后发现:

    同样执行10000次操作,使用hget 用时 6.27秒,用hmget 用时 1.28秒。

  2. 在lua 里面切换 redis的DB

    redis.call('select',   1);
    print(redis.call('get','aaa'));

    其中1表示 DB1,当这个值超过了redis 当前现有的DB数量的时候,会报错,可以试试


  3. redis 在 切换DB的时候,不同的连接是安全的,即 连接A切换到DB1的时候,连接B在不进行任何操作的情况下,还是指向DB0


  4. 未完待续。。。

Redis使用 Lua 脚本可以**实现原子性操作**,避免多命令竞态条件,同时减少网络开销(只需一次通信)。以下是详细使用方法及示例: --- ## **一、Lua 脚本基础语法** ### **1. 脚本执行命令** ```bash # 直接执行脚本 EVAL "return 'Hello, Redis!'" 0 # 使用参数 EVAL "return ARGV[1]" 0 "Hello" ``` - **`EVAL`**:执行脚本的核心命令。 - **参数说明**: - 第一个参数:Lua 脚本内容。 - 第二个参数:`KEYS` 的数量(即使不使用也必须声明)。 - 后续参数:`ARGV` 数组传递的额外参数。 --- ### **2. 键(KEYS)与参数(ARGV)** - **`KEYS`**:用于标识 Redis 中的键(保证原子性操作的资源)。 - **`ARGV`**:其他非键参数。 ```bash EVAL "return {KEYS[1], ARGV[1]}" 1 "user:1001" "name" ``` **输出**: ```json ["user:1001", "name"] ``` --- ## **二、实际应用场景与示例** ### **1. 原子性计数器递增** **需求**:实现一个带过期时间的计数器,避免竞态条件。 ```lua -- KEYS[1]: 计数器键 -- ARGV[1]: 过期时间(秒) -- ARGV[2]: 递增步长 local current = redis.call('GET', KEYS[1]) if not current then redis.call('SET', KEYS[1], 0, 'EX', ARGV[1]) end return redis.call('INCRBY', KEYS[1], ARGV[2]) ``` **执行命令**: ```bash EVAL "local current=redis.call('GET',KEYS[1]);if not current then redis.call('SET',KEYS[1],0,'EX',ARGV[1]) end;return redis.call('INCRBY',KEYS[1],ARGV[2])" 1 "counter:page_view" 60 1 ``` --- ### **2. 分布式锁实现** **需求**:获取锁时设置过期时间,避免死锁。 ```lua -- KEYS[1]: 锁键 -- ARGV[1]: 锁值(唯一标识) -- ARGV[2]: 过期时间(毫秒) if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]) return 1 -- 获取锁成功 else return 0 -- 获取锁失败 end ``` **执行命令**: ```bash 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 "lock:order" "uuid-1234" 5000 ``` --- ### **3. 限流器(滑动窗口)** **需求**:限制用户每分钟最多 10 次操作。 ```lua -- KEYS[1]: 限流键(如 "rate_limit:user123") -- ARGV[1]: 当前时间戳(毫秒) -- ARGV[2]: 窗口大小(毫秒,如 60000) -- ARGV[3]: 最大请求数(如 10) local key = KEYS[1] local now = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) local limit = tonumber(ARGV[3]) -- 清除过期请求 redis.call('ZREMRANGEBYSCORE', key, 0, now - window) -- 获取当前请求数 local count = redis.call('ZCARD', key) if count < limit then -- 允许请求,记录时间戳 redis.call('ZADD', key, now, now) redis.call('PEXPIRE', key, window) return 1 -- 允许 else return 0 -- 拒绝 end ``` **执行命令**: ```bash EVAL "local key=KEYS[1];local now=tonumber(ARGV[1]);local window=tonumber(ARGV[2]);local limit=tonumber(ARGV[3]);redis.call('ZREMRANGEBYSCORE',key,0,now-window);local count=redis.call('ZCARD',key);if count<limit then redis.call('ZADD',key,now,now);redis.call('PEXPIRE',key,window);return 1 else return 0 end" 1 "rate_limit:user123" 1630000000000 60000 10 ``` --- ## **三、性能优化技巧** ### **1. 使用 `SCRIPT LOAD` + `EVALSHA`** - **减少网络传输**:先加载脚本生成 SHA1 摘要,后续通过摘要调用。 ```bash # 加载脚本 SCRIPT LOAD "return redis.call('GET', KEYS[1])" # 返回: "abc123sha1hash..." # 通过 SHA1 执行 EVALSHA "abc123sha1hash..." 1 "user:1001" ``` ### **2. 避免长脚本** - Redis 是单线程模型,长脚本会阻塞其他命令。 - 复杂逻辑拆分为多个短脚本。 ### **3. 错误处理** - Lua 脚本中调用 Redis 命令出错时会抛出异常: ```lua local ok, err = pcall(redis.call, 'GET', 'nonexistent_key') if not ok then return "Error: " .. err end ``` --- ## **四、Java 集成示例(Spring Data Redis)** ```java import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; // 定义 Lua 脚本 String luaScript = "return redis.call('GET', KEYS[1])"; // 创建脚本对象 DefaultRedisScript<String> script = new DefaultRedisScript<>(); script.setScriptText(luaScript); script.setResultType(String.class); // 执行脚本 String result = redisTemplate.execute( script, Collections.singletonList("user:1001"), // KEYS "optional_arg" // ARGV ); ``` --- ## **五、注意事项** 1. **原子性**:Lua 脚本在 Redis 中单线程执行,天然原子性。 2. **调试**:脚本中可用 `redis.log(redis.LOG_NOTICE, "Debug message")` 输出日志。 3. **资源限制**: - 脚本默认最长执行时间:5秒(可通过 `lua-time-limit` 配置)。 - 超出限制后,Redis记录警告但**不会终止脚本**(需用 `SCRIPT KILL` 手动干预)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值