Redis 7.0中Lua脚本未优化反而被弱化,真正提升性能的是FUNCTION命令集:FUNCTION LOAD将函数持久化至AOF/RDB、自动复制到从节点,避免重启或主从切换后脚本丢失,实测网络传输减35%、QPS升22%。

Redis 7.0 中 Lua 脚本本身没有性能改进,反而被明确限制和弱化——真正替代它、带来实际性能收益的是 FUNCTION 命令集。
为什么 EVAL/EVALSHA 在 7.0+ 里反而更“危险”
很多人误以为 Redis 7.0 升级后 Lua 脚本变快了,其实恰恰相反:官方已将 SCRIPT LOAD 明确定义为“不持久、不同步、不复制”的临时缓存行为。这意味着:
-
EVALSHA在主节点重启后必然失败,报错NOSCRIPT No matching script - 主从切换后,新主节点无脚本缓存,
EVALSHA直接退化为全量传输EVAL,网络开销翻倍 - 集群环境下,脚本只存在于执行节点,跨 slot 调用
EVALSHA可能因路由错误而失败 - 运维必须手动在所有节点预加载脚本(
SCRIPT LOAD),脚本多时极易漏配或版本不一致
FUNCTION LOAD 才是 7.0 的真实性能提升点
FUNCTION LOAD 不是 Lua 的升级版,而是把脚本当作数据库对象来管理。它带来的可测量收益包括:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 脚本自动写入
AOF/RDB,重启、故障转移后仍可用,无需客户端 fallback 逻辑 - 函数自动复制到所有从节点,
FCALL在任意节点都可执行,不再依赖部署顺序 - 某电商实测:替换限流 Lua 后,网络传输量减少 35%,
QPS提升 22%,P99延迟从 8ms 降至 6ms - 函数名直接出现在
FUNCTION LIST输出中,调试、审计、ACL 授权都更清晰(如+function|call)
迁移旧 Lua 脚本到 FUNCTION 的关键动作
不能直接复用旧 EVAL 脚本,必须重写并注册。常见踩坑点:
- 必须以
#!lua name=mylib开头声明库名,否则FUNCTION LOAD报错ERR No functions registered - 必须显式调用
redis.register_function('func_name', handler),漏掉就FCALL找不到函数 -
FCALL myfunc 1 key1 100中的1是 keys 数量,不是参数个数;100是ARGV[1],不是硬编码 - 函数默认只读,若需写操作(如
INCR),必须确保未开启readonly模式,否则报错write commands not allowed - 覆盖已有函数必须加
REPLACE参数,否则报错ERR Library already exists
FUNCTION 和 EVAL 共存时的兼容性陷阱
虽然 EVAL 命令仍存在,但混用会放大风险:
-
EVAL脚本内不能调用redis.call('script load', ...)—— 这类动态加载在FUNCTION环境下被禁止 -
FUNCTION内无法使用redis.call('eval', ...)或redis.call('evalsha', ...),会报错ERR Unsupported command inside function -
SCRIPT FLUSH不影响已注册的FUNCTION,但会清空所有SCRIPT LOAD缓存,导致依赖它的EVALSHA批量失败 - 监控指标如
redis_scriptstat_*不再统计FUNCTION执行,需改用redis_functionstat_*新指标
真正需要关注的不是“Lua 怎么更快”,而是“怎么让服务在节点重启、主从切换、集群扩缩容后,脚本逻辑依然稳定可调用”。FUNCTION 解决的是运维可靠性问题,性能提升只是副产品;忽略这点,越优化越容易出线上事故。


















