lua脚本实现动态ttl的核心是将过期时间计算逻辑下推至redis端,基于实时数据原子性完成写入与设过期,避免竞态并提升缓存适应性。

缓存有效期结合 Lua 脚本实现动态计算,核心在于把过期时间(TTL)的决策逻辑从应用层下推到 Redis 服务端,在执行缓存写入(如 SET 或 SETEX)时,由 Lua 脚本根据实时数据(如请求参数、当前时间、业务状态等)算出一个合理的 TTL 值,再原子性地完成写入+设过期。
为什么需要 Lua 动态算 TTL?
静态 TTL(比如固定 5 分钟)无法适配真实场景:热点商品可能需缓存更久,冷门内容应快速过期;接口响应慢时可临时延长缓存,负载低时可缩短以保新鲜度。Lua 脚本能避免客户端与 Redis 多次往返,防止竞态(比如先查再算再写,中间可能被删或覆盖),保证“读取上下文 → 计算 TTL → 写入+设期”三步原子执行。
典型 Lua 脚本结构(带动态 TTL 计算)
以下是一个通用模板,可根据业务调整计算逻辑:
-- KEYS[1]: 缓存 key<br>
-- ARGV[1]: 实际 value(如 JSON 字符串)<br>
-- ARGV[2]: 可选基准 TTL(秒),供脚本参考<br>
-- ARGV[3]: 其他上下文参数,如 user_level、item_price、status 等<br><br>
local base_ttl = tonumber(ARGV[2]) or 300<br>
local context = ARGV[3]<br><br>
-- 示例逻辑:按用户等级动态调 TTL<br>
if context == "vip" then<br>
base_ttl = base_ttl * 2<br>
elseif context == "guest" then<br>
base_ttl = math.max(60, base_ttl / 2)<br>
end<br><br>
-- 示例逻辑:叠加热度因子(假设用 Redis 中已有的访问计数)<br>
local hit_count = tonumber(redis.call("GET", KEYS[1] .. ":hits")) or 0<br>
base_ttl = base_ttl + hit_count * 10<br><br>
-- 最终 TTL 不超过 1 小时,不低于 30 秒<br>
base_ttl = math.min(3600, math.max(30, base_ttl))<br><br>
-- 原子写入 + 设置过期<br>
redis.call("SET", KEYS[1], ARGV[1])<br>
redis.call("EXPIRE", KEYS[1], base_ttl)<br><br>
return base_ttl
在应用中调用的关键点
-
预加载脚本:使用
EVALSHA提升性能,首次用SCRIPT LOAD加载,后续传 sha1 值调用,避免每次传输脚本文本 -
参数传递要精简:KEYS 只传 key(必须是 Redis 键名),复杂上下文尽量压缩成字符串(如
"vip:123:active")放 ARGV,避免在 Lua 里解析大 JSON -
避免耗时操作:Lua 中不能调用 sleep、IO 或外部 API;所有依赖数据(如热度、状态)需提前存在 Redis 中(例如用
INCR维护访问计数),由脚本直接GET/HGET获取 -
兜底 TTL:务必对计算结果做上下限约束(如
math.max(30, math.min(3600, ttl))),防逻辑异常导致缓存永不过期或瞬间失效
常见适用场景举例
- 分层用户缓存:VIP 用户缓存时间翻倍,游客减半,依据登录态 token 解析出的 user_type 字段动态决定
- 价格类数据缓存:商品价格变动频繁时,若当前价格距上次更新不足 1 小时,TTL 设为 300 秒;否则设为 3600 秒
- 降级开关联动:当全局降级开关开启(Redis 中某 key 存在),自动将所有相关缓存 TTL 缩短至 60 秒,加快配置生效
-
时间窗口敏感数据:如“今日热搜榜”,TTL 设为
86400 - (当前秒级时间 % 86400),确保每天 0 点准时刷新











