一、为什么需要缓存模块?
某电商平台在促销期间每秒收到5万次商品详情请求,数据库每秒只能处理8千次查询。当技术团队引入OpenResty的共享字典缓存后,请求响应时间从300ms骤降到8ms,数据库负载降低87%——这就是缓存的魔力。
二、OpenResty缓存核心组件
1. 内存缓存模块
-- 初始化内存缓存
local cache = ngx.shared.my_cache
-- 设置缓存(带过期时间)
local success, err = cache:set("product_123", "库存数据", 60) -- 60秒过期
if not success then
ngx.log(ngx.ERR, "缓存设置失败:", err)
end
-- 获取缓存
local product_data = cache:get("product_123")
if product_data then
ngx.say("缓存命中:", product_data)
return
end
此示例展示基础的内存缓存操作,适合高频访问但更新不频繁的数据。注意设置合理的TTL(生存时间)避免数据过期问题。
2. LRU缓存实践
-- 创建LRU缓存实例
local lru = require "resty.lrucache"
local cache, err = lru.new(200) -- 最多缓存200个对象
-- 设置缓存
cache:set("user_profile_456", {name="张三", level="VIP5"}, 0) -- 0表示永不过期
-- 智能获取模式
local profile = cache:get("user_profile_456")
if not profile then
profile = fetch_from_db(456)
cache:set("user_profile_456", profile)
end
LRU缓存适合需要自动淘汰机制的场景。注意当缓存对象较大时,需要合理设置缓存容量。
三、进阶缓存策略
3. 多级缓存架构
location /api/products {
access_by_lua_block {
local cache = ngx.shared.product_cache
local redis = require "resty.redis"
local red = redis:new()
-- 第一层:内存缓存
local data = cache:get(ngx.var.arg_id)
if data then
ngx.say(data)
return
end
-- 第二层:Redis缓存
local ok, err = red:connect("127.0.0.1", 6379)
if ok then
data = red:get("product:"..ngx.var.arg_id)
if data ~= ngx.null then
cache:set(ngx.var.arg_id, data, 10) -- 回写本地缓存
ngx.say(data)
return
end
end
-- 第三层:数据库查询
data = query_db(ngx.var.arg_id)
red:set("product:"..ngx.var.arg_id, data)
cache:set(ngx.var.arg_id, data, 5) -- 短期缓存
ngx.say(data)
}
}
这种三级缓存架构结合了内存、Redis和数据库,适合需要极高并发能力的场景。注意不同层级的缓存时间设置策略。
4. 缓存穿透防护
local shared_cache = ngx.shared.protected_cache
local function get_with_protection(key)
local value = shared_cache:get(key)
if value then
return value
end
-- 获取互斥锁
local lock_key = "lock:"..key
if shared_cache:add(lock_key, true, 2) then -- 2秒锁定时长
-- 数据库查询
value = query_db(key)
-- 空值缓存防护
if value then
shared_cache:set(key, value, 60)
else
shared_cache:set(key, "NIL", 30) -- 缓存空值
end
shared_cache:delete(lock_key)
return value
else
-- 等待其他线程完成查询
ngx.sleep(0.1)
return get_with_protection(key)
end
end
这段代码有效防止缓存穿透和击穿问题,通过互斥锁和空值缓存机制保障系统稳定性。
四、性能优化技巧
5. 批量缓存操作
-- 批量获取商品信息
local keys = {"p_123", "p_456", "p_789"}
local results = {}
-- 创建批量操作表
local ops = {}
for i, key in ipairs(keys) do
table.insert(ops, {"get", key})
end
-- 执行批量操作
local res, err = ngx.shared.product_cache:mget(ops)
if res then
for i, key in ipairs(keys) do
results[key] = res[i]
end
end
-- 处理未命中情况
for _, key in ipairs(keys) do
if not results[key] then
results[key] = fetch_from_db(key)
ngx.shared.product_cache:set(key, results[key], 60)
end
end
批量操作减少网络开销,特别适合需要同时获取多个缓存项的场景。注意处理部分命中和完全未命中的不同情况。
6. 缓存预热策略
init_worker_by_lua_block {
local delay = 5 -- 延迟5秒执行
local handler = function()
local hot_keys = {"top_product", "home_banner", "news_list"}
local cache = ngx.shared.warmup_cache
for _, key in ipairs(hot_keys) do
local data = fetch_hot_data(key)
cache:set(key, data, 3600) -- 缓存1小时
end
end
local ok, err = ngx.timer.at(delay, handler)
if not ok then
ngx.log(ngx.ERR, "缓存预热失败:", err)
end
}
在worker进程启动时预热关键缓存,避免冷启动导致的性能问题。注意控制预热数据的范围和频率。
五、实战经验总结
应用场景分析
- 电商秒杀系统:使用内存缓存+原子计数器
- 新闻门户:多级缓存架构+智能过期策略
- API网关:JWT令牌缓存+请求结果缓存
- 实时监控:短时高并发数据缓存
技术优缺点
优势:
- 内存级访问速度(微秒级响应)
- 支持原子操作(incr/decr)
- 灵活的过期策略
- 与Nginx事件模型完美结合
局限:
- 单worker内存限制
- 集群同步需要额外方案
- 大对象存储性能下降
- 调试复杂度较高
核心注意事项
- 内存分配:建议预留30%内存空间
- 键值设计:采用业务前缀(如user:123)
- 监控指标:关注命中率、淘汰率
- 失效策略:结合主动更新和被动过期
- 压力测试:模拟极端场景下的表现
六、终极缓存方案
location /smart_cache {
content_by_lua_block {
local cache = require "resty.cache"
local c = cache.new{
name = "smart_cache",
serializer = require("cjson").encode,
deserializer = require("cjson").decode,
storage = {
{ "shared_dict", "my_cache", 10 }, -- 10MB空间
{ "lru", 1000 }, -- 1000个对象
{ "redis", { host = "127.0.0.1", port = 6379 }}
}
}
local data, err = c:get(ngx.var.request_uri)
if not data then
data = generate_response()
c:set(ngx.var.request_uri, data, { ttl = 60, tags = {"api"} })
end
ngx.say(data)
}
}
这个智能缓存系统自动选择最佳存储层级,支持序列化和标签管理,是OpenResty缓存的集大成方案。