openresty通过分层协同、元数据驱动和原子操作实现动态内容缓存与精准清除:用lua_shared_dict做本地缓存,lua控制写入策略,索引结构支持前缀/时间桶清理,内网接口校验触发,保障一致性与安全性。

OpenResty 通过 Lua 脚本实现动态内容缓存与缓存清除,核心在于**分层协同 + 元数据驱动 + 原子操作**:用 lua_shared_dict 做高性能本地缓存,用 Lua 控制缓存写入时机与策略,再通过显式记录的索引结构(而非暴力遍历)实现精准清除。不依赖外部服务,也不需要重启 Nginx。
动态内容缓存:按需写入、多级适配
动态内容(如用户个性化推荐、实时搜索结果)不适合全量预热,应“按请求生成、按规则缓存”:
- 在
content_by_lua_block中判断是否命中缓存;未命中则调用后端(HTTP/Redis/DB),拿到响应后立即写入ngx.shared.my_cache - 缓存 key 需包含动态因子,例如:
"user:" .. ngx.var.cookie_uid .. ":search:" .. ngx.md5(ngx.var.args.q),避免不同用户或参数混用 - 对时效敏感的内容(如 30 秒内热搜),设置短 TTL(如
cache:set(key, val, 30));对稳定性要求高的,可配合get_stale实现“过期仍可用 + 异步刷新” - 建议为不同业务划分独立 shared dict,例如:
lua_shared_dict user_cache 8m;、lua_shared_dict search_cache 16m;,防止互相挤占内存
缓存清除:前缀清理与时间桶清理
OpenResty 的 shared dict 不支持 keys(),不能扫表删除,必须靠“提前登记 + 按图索骥”:
-
前缀索引法:写缓存时同步更新索引。例如存
product:1001,同时向idx:product这个 shared dict 中追加该 key(用cjson.encode({ "product:1001", "product:1002" })存为数组字符串);清除时先get索引,再逐个delete对应 key,最后清空索引本身 -
时间分片桶法:适用于按时间窗口失效的场景(如“清除 5 分钟前的所有搜索缓存”)。将当前分钟哈希为桶名(如
"bucket:2026090311"),写缓存时同时写入主 dict 和该桶(bucket_dict:add("bucket:2026090311", key));清理旧桶即可批量失效,无需查 key 列表 - 索引 dict 应单独配置(如
lua_shared_dict idx 2m;),避免与主缓存争资源;索引值建议用 JSON 序列化,便于跨 worker 一致读取
安全触发清除:内网接口 + 参数校验
清除操作不可暴露给公网,需受控触发:
- 配置仅限内网访问的管理 location:
location /admin/cache/clear { allow 10.0.0.0/8; deny all; content_by_lua_block { ... } } - 支持 query 参数控制范围,例如:
?prefix=user:12345或?bucket=2026090310,Lua 脚本解析后调用对应清除函数 - 关键操作前加日志和计数:
ngx.log(ngx.INFO, "clearing ", prefix, " — ", count, " keys deleted"),便于审计与监控 - 避免误删,清除前可加白名单校验(如只允许清除以
user:、search:开头的前缀)
整个机制不复杂但容易忽略元数据一致性——写缓存和写索引必须成对出现,建议封装为原子函数,失败时回滚或告警。











