1. 当我们聊缓存时在聊什么?

想象你经营着一家网红奶茶店,每次顾客点单都要从仓库拿原料(这就像服务器每次都要访问数据库)。但聪明的店长会把常用原料放在前台货架(这就是缓存),OpenResty就是这个帮你搭建智能货架的技术管家。它基于Nginx和LuaJIT,能用Lua脚本灵活控制缓存逻辑。

2. 快速搭建缓存工作台

2.1 基础配置(技术栈:OpenResty 1.21+)

http {
    # 加载Lua模块
    lua_package_path "/usr/local/openresty/lualib/?.lua;;";
    
    # 创建共享字典(相当于公共储物柜)
    lua_shared_dict my_cache 128m;  # 128MB内存空间
    
    server {
        listen 80;
        
        location /api {
            # 交给Lua脚本处理
            content_by_lua_block {
                -- 后续章节的代码都会放在这里
            }
        }
    }
}

2.2 缓存读写示例

-- 获取请求参数
local product_id = ngx.var.arg_id

-- 从缓存储物柜拿数据
local cache = ngx.shared.my_cache
local cached_data = cache:get(product_id)

if cached_data then
    ngx.say("从缓存获取:", cached_data)
    return
end

-- 缓存未命中时查询数据库(模拟)
local db_data = "商品详情_"..product_id
ngx.sleep(0.1)  -- 模拟数据库查询耗时

-- 存入缓存(设置5分钟有效期)
cache:set(product_id, db_data, 300)
ngx.say("从数据库获取:", db_data)

3. 高阶玩法:缓存失效策略

3.1 批量清除缓存

-- 接收POST请求清除指定缓存
if ngx.req.get_method() == "POST" then
    ngx.req.read_body()
    local post_args = ngx.req.get_post_args()
    
    -- 遍历清除多个key
    for key, _ in pairs(post_args) do
        ngx.shared.my_cache:delete(key)
        ngx.log(ngx.INFO, "已清除缓存:", key)
    end
    ngx.say("缓存清理完成")
    return
end

3.2 缓存预热策略

-- 启动时预热热点数据
init_worker_by_lua_block {
    local cache = ngx.shared.my_cache
    local hot_items = {"top1", "top2", "top3"}
    
    for _, item in ipairs(hot_items) do
        if not cache:get(item) then
            cache:set(item, "预热数据_"..item, 600)
        end
    end
}

4. 应用场景分析

4.1 高并发读取场景

  • 典型场景:电商秒杀商品详情页
  • 实战技巧:采用"缓存穿透保护+异步更新"组合拳
-- 带锁查询防止缓存击穿
local lock_key = product_id.."_lock"
if not cache:get(product_id) then
    if cache:add(lock_key, true, 2) then  -- 设置2秒锁
        -- 只有获得锁的请求去查库
        local db_data = query_db(product_id)
        cache:set(product_id, db_data, 300)
        cache:delete(lock_key)
    else
        -- 其他请求等待100ms后重试
        ngx.sleep(0.1)
        cached_data = cache:get(product_id)
    end
end

4.2 动态内容缓存

  • 典型场景:个性化推荐系统
  • 处理方案:带参数签名校验
-- 生成参数签名
local params = ngx.req.get_uri_args()
local signature = ngx.md5(ngx.encode_args(params))

-- 使用签名作为缓存key
local cache_key = "recommend_"..signature
local data = cache:get(cache_key)

if not data then
    data = generate_recommend(params)
    cache:set(cache_key, data, 60)  -- 短时间缓存
end

5. 技术选型优劣谈

5.1 闪光点

  • 速度王者:内存级响应(<1ms访问速度)
  • 容量自由:支持GB级内存分配
  • 灵活之王:Lua脚本实现复杂逻辑
  • 集群支持:可与Redis组成二级缓存

5.2 需要注意的坑

  • 内存刺客:不控制的话容易撑爆内存
  • 雪崩风险:批量失效可能引发数据库压力
  • 数据一致:需要自己实现同步机制
  • 类型限制:只能存储字符串类型数据

6. 避坑指南

  1. 内存水位监控:建议设置共享字典使用率告警
  2. Key命名规范:建议使用"业务_模块_参数"结构
  3. 过期时间随机化:避免同一时间大量失效
  4. 版本兼容性:注意不同OpenResty版本的API差异
  5. 压力测试:建议使用wrk进行基准测试

7. 总结与展望

OpenResty的缓存就像给服务器装上了涡轮增压器,通过本文的配置示例和场景分析,我们可以看到它特别适合需要快速响应但数据更新不频繁的场景。虽然需要开发者自己把控很多细节,但带来的性能提升是显著的。

未来发展方向可以关注:与Kubernetes生态的深度整合、自动内存优化算法的引入、机器学习驱动的智能缓存策略等。建议读者从简单的页面缓存开始实践,逐步过渡到复杂的业务场景,同时注意做好监控和熔断机制。