一、为什么需要缓存模块?

某电商平台在促销期间每秒收到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令牌缓存+请求结果缓存
  • 实时监控:短时高并发数据缓存

技术优缺点

优势:

  1. 内存级访问速度(微秒级响应)
  2. 支持原子操作(incr/decr)
  3. 灵活的过期策略
  4. 与Nginx事件模型完美结合

局限:

  1. 单worker内存限制
  2. 集群同步需要额外方案
  3. 大对象存储性能下降
  4. 调试复杂度较高

核心注意事项

  1. 内存分配:建议预留30%内存空间
  2. 键值设计:采用业务前缀(如user:123)
  3. 监控指标:关注命中率、淘汰率
  4. 失效策略:结合主动更新和被动过期
  5. 压力测试:模拟极端场景下的表现

六、终极缓存方案

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缓存的集大成方案。