redis functions比eval更可靠,因其函数持久化至aof/rdb并自动复制到从节点,避免主从切换或重启后脚本丢失;需显式声明#!lua头、禁用全局变量和非安全模块;执行原子但阻塞单线程,须规避i/o和长循环;acl需显式授权+function等权限。

Redis Functions比EVAL更可靠,因为函数会持久化和复制
传统 EVAL 和 EVALSHA 执行的 Lua 脚本只存在内存里,一旦发生 SCRIPT FLUSH、主从切换或 Redis 重启,脚本就丢了。客户端必须重新 SCRIPT LOAD,否则调用 EVALSHA 会报错 NOSCRIPT No matching script。这不是偶发问题——在生产集群里,主备故障转移后脚本消失是常态。
而 FUNCTION LOAD 注册的函数库会写入 AOF、参与 RDB 快照,并自动同步到所有从节点。你 reload 一次,它就在整个集群里“活”着。
- 函数库名(如
lib:inventory)+ 函数名(如decr_stock)构成唯一标识,调用时用FCALL inventory.decr_stock 1 item:123 10 - 不支持单个函数热更新;改一个函数就得整库重载,避免版本错乱
-
FUNCTION LIST可查已加载函数,FUNCTION STATS能看执行次数和错误率,可观测性远超旧脚本
怎么把老EVAL脚本迁成Functions:三步不能跳
不是简单把 EVAL 换成 FCALL 就完事。Functions 对 Lua 环境做了收紧,直接迁移大概率报错 Lua redis() command arguments must be strings or integers 或 user_script:1: attempt to call a nil value (global 'redis')。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 必须显式声明
#!lua name=lib:xxx头,且只能出现在第一行(空行都不行) - 不能用
redis.call(),得用redis.call("INCR", key)这种全小写命令名;大写如REDIS.CALL会失败 - 禁止访问全局变量(如
math.randomseed(os.time())中的os),也不支持io、loadstring等非安全模块 - 示例合法头:
#!lua name=lib:cart version=1,然后跟函数定义:redis.register_function("add_item", function(keys, args) ... end)
Functions执行是原子的,但慢函数会卡住整个Redis
Functions 和 EVAL 一样,在执行期间 Redis 单线程会被独占。如果函数里做 HTTP 请求、循环百万次、或调用 KEYS *,后果就是所有其他命令排队等待,延迟飙升,监控里看到 latency spikes。
- 别在 Functions 里做任何阻塞操作:无网络 I/O、无文件读写、无长循环
- 复杂计算尽量拆解:比如排行榜更新,先用
ZADD写分值,再用单独的后台任务聚合统计,别一股脑塞进一个 Function - 用
redis.log(redis.LOG_WARNING, "debug info")打日志,但别高频打;日志内容会进 Redis 日志文件,刷太多影响性能 - 测试时加
redis.setresp(2)确保返回格式兼容旧客户端(尤其用 jedis/lettuce 的 Java 项目)
ACL权限控制让Functions更安全,但也更易配错
Functions 默认继承调用者的 ACL 权限,但你可以为函数库单独设权限。常见误配是:给用户开了 read 权限,却忘了 function 命令本身需要 +function 权限,结果用户一调 FCALL 就报 (error) NOPERM this user has no permissions to run the 'fcall' command。
- ACL 规则要显式加
+function(允许调用)、+function|load(允许注册)、+function|delete(允许删库) - 函数内部调用的 Redis 命令,权限检查发生在运行时——比如函数里用了
DEL,用户 ACL 就必须含+del,否则运行时报错 - 调试建议:先用
ACL WHOAMI看当前用户权限,再用ACL LIST核对是否漏了+function










