一、为什么我们需要缓存?

咱们都有过这样的体验:每次打开淘宝看商品详情时,页面加载速度嗖嗖的快。这背后就有缓存的功劳。在OpenResty生态中,Lua脚本与Nginx的深度整合,让缓存机制就像给服务器装上了涡轮增压器。

想象这样的场景:每当用户请求商品详情时,如果每次都查数据库,数据库迟早会被压垮。这时候咱们的缓存层就派上用场了——它就像个超级记忆体,把常用数据存在离CPU最近的地方。

二、OpenResty的缓存

2.1 共享字典(shared dict)

这是OpenResty自带的缓存神器,基于内存的键值存储。在nginx.conf里这样配置:

http {
    lua_shared_dict my_cache 128m;  # 创建128MB的共享内存区
}

对应的Lua操作示例:

-- 获取共享字典句柄
local cache = ngx.shared.my_cache

-- 写入缓存(带5分钟过期时间)
local success, err = cache:set("product_123", "{name:'手机',price:3999}", 300)
if not success then
    ngx.log(ngx.ERR, "缓存写入失败:", err)
end

-- 读取缓存
local value = cache:get("product_123")
if value then
    ngx.say("从缓存获取:", value)
else
    -- 数据库查询逻辑...
end

2.2 LRU缓存策略实战

当内存吃紧时,咱们需要淘汰机制。下面这个示例实现最近最少使用策略:

local lrucache = require "resty.lrucache"
local cache, err = lrucache.new(200)  -- 最多缓存200个对象

-- 添加缓存项
cache:set("user_888", {name="张三", vip_level=3})

-- 获取时自动更新热度
local user = cache:get("user_888")
if user then
    ngx.say(user.name.."是VIP", user.vip_level)
end

2.3 缓存雪崩预防方案

当大量缓存同时失效时会引发雪崩,解决方法就像给缓存失效时间加个随机扰动:

local function get_with_avalanche_protection(key)
    local value = cache:get(key)
    if not value then
        value = fetch_from_db(key)
        -- 基础300秒 + 随机60秒扰动
        local ttl = 300 + math.random(60)
        cache:set(key, value, ttl)
    end
    return value
end

三、缓存应用

3.1 热点数据缓存

适合商品详情、用户信息等查询频繁的数据。注意设置合理的过期时间,就像给牛奶标注保质期:

-- 商品信息缓存示例
local product_cache_key = "product_"..ngx.var.product_id
local product = cache:get(product_cache_key)
if not product then
    product = query_product(ngx.var.product_id)
    cache:set(product_cache_key, product, 600)  -- 缓存10分钟
end

3.2 页面片段缓存

对于动态页面中的固定部分,比如导航栏:

-- 缓存导航HTML片段
local nav_html = cache:get("global_nav")
if not nav_html then
    nav_html = render_nav()
    cache:set("global_nav", nav_html, 3600)  -- 缓存1小时
end
ngx.print(nav_html)

四、缓存双刃剑的辩证使用

4.1 优势分析

  • 速度对比:内存访问比SSD快100倍以上
  • QPS提升:某电商案例显示缓存使并发能力提升8倍
  • 成本节约:减少70%的数据库调用

4.2 避坑指南

  • 穿透防护:当查询不存在的数据时,使用空值标记
if not db_result then
    cache:set(key, "NULL", 60)  -- 特殊标记防穿透
end
  • 更新策略:采用先更新数据库再失效缓存的双删策略
  • 监控指标:建议监控缓存命中率、内存使用率等核心指标

五、缓存与数据库的太极之道

数据一致性是永恒的话题。推荐采用异步更新策略:

-- 数据库更新后异步刷新缓存
local ok, err = cache:set(key, new_value)
if not ok then
    ngx.log(ngx.ERR, "缓存更新失败:", err)
    -- 加入重试队列
    local res = ngx.location.capture("/retry_queue", {method=ngx.HTTP_POST})
end

六、缓存进阶秘籍

6.1 批量操作优化

使用管道技术提升效率:

local entries = {
    {key="user_1", value="Alice"},
    {key="user_2", value="Bob"}
}

cache:flush_all()  -- 清空缓存
for _, entry in ipairs(entries) do
    cache:set(entry.key, entry.value)
end

6.2 内存优化技巧

当缓存大对象时,使用序列化压缩:

local cjson = require "cjson"
local data = {big_table}  -- 假设是大数据表
local compressed = cjson.encode(data)
cache:set("big_data", compressed)

七、最佳实践路线

  1. 按业务场景选择缓存策略
  2. 建立分层缓存体系(内存->Redis->DB)
  3. 实施监控告警机制
  4. 定期进行缓存有效性验证
  5. 制定缓存更新SOP(标准操作流程)