Redis 7重大变更:Lua不再持久化和复制了可能导致evalsha失效

一、关于evalsha的谣言

1、eval 和 evalsha基本使用

关于Redis lua的两个命令eval和evalsha,网上大多数文章都会介绍,eval带的参数是lua脚本内容,evalsha可以通过script load把lua脚本加载到Redis缓存起来,推荐使用evalsha**(性能更强)**

(1)、 eval
eval script numkeys [key [key ...]] [arg [arg ...]]

例如简单命令

eval "return ARGV[1]" 0 hello
"hello"
(2)、 evalsha

(a) 脚本

> cat xxx.lua
return 'Immabe a cached script'

(b) 上传脚本返回sha

redis> script load xxx.lua
"c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f"

(c) 直接执行sha

redis> evalsha c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f 0
"Immabe a cached script"
2、evalsha性能确实更强!

这里给出一个lua脚本,是在没有Redis 6.2 zadd gt|lt功能前需要用lua来实现:

local var = redis.call('zscore',KEYS[1], ARGV[1])
if((not var) or tonumber(var) < tonumber(ARGV[2])) then
  redis.call('zadd',KEYS[1],ARGV[2],ARGV[1])
end
return redis.call('zcard',KEYS[1])

类似实现:

图片

(1) eval
 ./memtier_benchmark -s 10.108.203.169 -p 12615 --requests=50000 -c 10 --hide-histogram --command="eval \"local var = redis.call('zscore',KEYS[1], ARGV[1]) if( (not var) or tonumber(var) < tonumber(ARGV[2])) then redis.call('zadd',KEYS[1],ARGV[2],ARGV[1]) end return redis.call('zcard',KEYS[1])\" 1 score-rank zhangsan 10"

结果:

图片

(2) evalsha
./memtier_benchmark -s 10.108.203.169 -p 12615 --requests=50000 -c 10 --hide-histogram --command="evalsha 2d78dd089e00088bb4a3daf07b8c4ec45dcba32e 1 score-rank zhangsan 10"

结果:

图片

结论:确实evalsha性能会更好一些,大概10%-20%左右,这里我没测试复杂的脚本,但必须说一句用lua场景拼性能不太合适,我理解redis中的lua是一个辅助功能,如果要拼性能可以自定义命令或者使用module来实现更好

3、各种用法

(1) 迷信evalsha的
  • 使用Redis主从结构:知道Redis主节点,使用script load加载lua脚本获取到${sha1}

  • 使用集群模式,找DBA同学帮忙加载进去的(这种都埋坑,还记得 Redis只有script sha,还能找到lua脚本吗

(2) 不信evalsha的,觉得eval可靠的
redis.eval("xxxxx");
(3) 组合使用
try{
      //自己计算sha1
     redis.eval(sha1); 
} catch (Exception e) {
    if ("NOSCRIPT No matching script"){
        redis.eval
    }
}

这里有个冷知识,但是非常实用,那就是执行eval也可以生成sha1,下面我们做一个实验

127.0.0.1:12615> script load "return 'hello lua lua'"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
127.0.0.1:12615> evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
127.0.0.1:12615> info memory
number_of_cached_scripts:1
127.0.0.1:12615> script flush
OK
127.0.0.1:12615> evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
(error) NOSCRIPT No matching script. Please use EVAL.
127.0.0.1:12615> info memory
used_memory_scripts:0

继续用eval来测试

127.0.0.1:12615> eval "return 'hello lua lua'" 0
"hello lua lua"
127.0.0.1:12615> evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
127.0.0.1:12615> info memory
number_of_cached_scripts:1

其实就是eval也会缓存,这个在官网里有说明,可能以前看的不够仔细

图片

二、Redis 官方对eval的认知

其实官方对evalsha或者script cache的认知已经很清楚,这玩意就是缓存,不想保障SLA,因此在github上也有类似讨论,其实在Redis 5中已经添加了lua-replicate-commands(默认是yes),保持script cache具备SLA,https://github.com/redis/redis/pull/9812有相关讨论:

图片

上面这个讨论已经说的很清楚:大概就是5.0用lua-replicate-commands先保证script cache SLA,但是也是临时的,3年后的Redis 7.0不保证了,给了一个缓冲期(但一般不会看的这么细。。)。

图片

三、Redis 7.0的关于lua重要变化

1、RDB文件不在保存lua脚本

我做了一组实验,确实在Redis 7.0的RDB里看不到lua脚本了?

(1) xxd 6.0.15.rdb

图片

(2) xxd 6.2.13.rdb

图片

(3) xxd 7.0.11.rdb

图片

有人说这个RDB的lua有啥用呢,我知道的可能有两个作用

(1) lua是个不错的功能,但可能是杀手

我们知道在redis里如果lua执行时间较长(lua-time-limit),redis必须使用shutdown才能恢复。所以一些大厂会让业务在使用lua前进行登记,或者对于存量可能采用分析RDB来统计

(2) 保住only evalsha:有时候业务是没错的,错的是Redis维护方不行

如果涉及到集群同步(仅同步、扩容、升级),需要在模拟复制过程中解析RDB文件,同步script cache(其实就是执行eval)

图片

2、Redis主从不同步script cache

我又做了一组实验,在所有主节点上加载lua:

$ sh script-load.sh
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"
"30c1c434e1d7aa18dd38735615b2fdf063306715"

在不同版本的slave节点上执行evalsha:

3.0.7、3.2.13、4.0.14、5.0.14、6.0.15、6.2.13

./redis-cli -p 13307 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
./redis-cli -p 13313 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
./redis-cli -p 13414 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
./redis-cli -p 13514 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
./redis-cli -p 13615 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"
./redis-cli -p 13623 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
"hello lua lua"

7.0.11、7.2.7、7.4.2

./redis-cli -p 13711 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
(error) NOSCRIPT No matching script. Please use EVAL.
./redis-cli -p 13727 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
(error) NOSCRIPT No matching script. Please use EVAL.
./redis-cli -p 13742 evalsha 30c1c434e1d7aa18dd38735615b2fdf063306715 0
(error) NOSCRIPT No matching script. Please use EVAL.

结论:从节点上确实无法正确执行evalsha

3、更全的change

图片

四、如何应对Redis 7.0?

2023~2024两年线上已经有上千个Redis 7.0集群,未发生这样的问题,还真得感谢业务同学代码写的代码相对规范,但也不可以掉以轻心,因为确实还有这样的.....Redis只有script sha,还能找到lua脚本吗?,也有这样的break change 升级Redis 7一个诡异问题定位

  • 增量:管控lua脚本的使用(平台介入)、日常宣讲、Redis客户端测对only evalsha加以限制

  • 存量:改造平台和相关中间件兼容Redis 7.0+、监控only evalsha进行整改等。

升级Redis 7.0是我碰到问题最多的一次,但是升级版本带来的收益也是显而易见的,所以历史前进的车轮不会停下的,希望早点用上Redis 8.0。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值