一、为什么我们需要缓存?
咱们都有过这样的体验:每次打开淘宝看商品详情时,页面加载速度嗖嗖的快。这背后就有缓存的功劳。在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)
七、最佳实践路线
- 按业务场景选择缓存策略
- 建立分层缓存体系(内存->Redis->DB)
- 实施监控告警机制
- 定期进行缓存有效性验证
- 制定缓存更新SOP(标准操作流程)