function load必须以#!lua name=xxx开头且无空行、注释或bom,禁用全局redis表和大写命令;防穿透需用exists+get组合避免keys/scan;fcall传参须严格区分keys与args;acl需显式授权+function/+fcall;热更新仅支持整库replace。

FUNCTION LOAD必须带#!lua头且只能在第一行
直接把老EVAL脚本内容丢给FUNCTION LOAD会报错user_script:1: attempt to call a nil value (global 'redis'),因为Functions禁用了全局redis表访问,也不认大写命令。必须从第一行开始写#!lua name=lib:anti-pen,中间不能有空行、注释或BOM。名字里别用点号(如anti.pen)——Redis只认ASCII字母、数字和下划线。
常见错误:
- 头行写了
// #!lua name=xxx→ 注释导致解析失败 -
#!lua name=lib:rate.limit→ 点号非法,改用lib_rate_limit - 脚本开头有UTF-8 BOM → Redis读到不可见字符,直接拒绝加载
防穿透函数要避开KEYS和SCAN,用EXISTS+GET组合判断
缓存穿透本质是查一个根本不存在的key,比如恶意请求user:id:999999999。传统方案用布隆过滤器,但Functions里没法调用外部模块,得靠原子逻辑兜底:先EXISTS确认key是否存在,再GET取值,最后根据结果决定是否回源或写空值。不能用KEYS user:id:*——它会遍历全库,阻塞主线程几秒甚至更久。
实操要点:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 用
redis.call("EXISTS", keys[1]) == 1代替redis.call("GET", keys[1]) ~= false,前者更快且不触发慢日志 - 空值缓存必须设短过期(如
redis.call("SET", keys[1], "__MISS__", "EX", 60)),避免占满内存 - 如果业务允许,把空值key加前缀隔离(如
miss:user:id:999999999),方便批量清理
FCALL调用时keys和args传参不能混淆
FCALL anti-pen.check 1 user:id:123中的1是keys数量,不是参数。所有key必须放在keys数组里,其他条件(比如空值过期时间、回源开关)得走args。函数里误把args[1]当key用,会导致ERR Error running script (call to f_...): @user_script:3: @user_script: 3: WRONGTYPE Operation against a key holding the wrong kind of value这类错——因为实际查的是一个字符串值而非key。
正确结构示例:
#!lua name=lib:anti-pen
redis.register_function("check", function(keys, args)
local key = keys[1]
local ttl = tonumber(args[1]) or 60
local exists = redis.call("EXISTS", key)
if exists == 1 then
return redis.call("GET", key)
else
-- 写空值并返回nil,由客户端决定是否回源
redis.call("SET", key, "__MISS__", "EX", ttl)
return nil
end
end)
ACL权限和可观测性配置不能漏
没开+function权限,FCALL会直接报NOPERM this user has no permissions to run the 'fcall' command。光开+read不够,必须显式授权:ACL SETUSER myapp +function +fcall +flushdb。另外,上线后别忘了用FUNCTION STATS盯执行错误率——如果error_count持续上涨,大概率是某个args传了非数字导致tonumber()返回nil,后续redis.call("SET", ..., "EX", nil)就崩了。
容易被忽略的点:
- 函数里用
redis.log(redis.LOG_WARNING, ...)打调试日志,但生产环境别打高频key名,否则日志文件涨得比AOF还快 -
FUNCTION LIST返回的description字段为空时,监控系统很难区分函数用途,建议在redis.register_function里补description="cache miss guard" - 热更新只能整库
REPLACE,改一行就得重发全部函数,别在线上频繁操作










