华为云 DCS for Redis 缓存治理实战:分布式锁、穿透防护与雪崩兜底
背景:一次商品详情页的读放大
双十一预热时我们的商品详情接口 QPS 冲到 3000,后端 RDS for MySQL 只有 4C8G,CPU 连续二十分钟顶在 95%,P99 从 80ms 涨到 1.2 秒。排查发现 SQL 并不慢,问题在于热点商品的读请求几乎完全重复,却每次都要走完整个链路。于是我们引入华为云 DCS for Redis 做缓存优先改造,改造后数据库读 QPS 下降约八成,P99 回落到 60ms 以内。下面记录落地过程。重点讲三个必须自己写代码解决的问题:穿透、击穿和雪崩。
第一步:实例与连接
在 DCS 控制台创建 Redis 5.0 主备实例,部署在与 ECS 相同的 VPC,关闭公网访问,只把业务子网加入白名单。客户端用 redis-py,关键是连接池与超时预算:socket_timeout 必须小于接口的整体超时,否则缓存抖动会连带拖垮整个请求。
pool = redis.ConnectionPool(host=ADDR, port=6379, password=PWD,
max_connections=50, socket_timeout=0.3, socket_connect_timeout=0.3)
第二步:穿透的空值兜底
穿透指查询一个根本不存在的 id,缓存永不命中,每次都落到数据库,被扫 id 时数据库会直接被压死。我们做三层防护:参数合法性校验、空值短缓存、外层布隆过滤器。命中空结果时写入六十秒的空标记,避免同一无效 id 反复回源;但 TTL 不能太长,否则商品新上架后会有最长一分钟的查不到窗口。无效 id 反复回源,等于让数据库白承担一堆无效查询。
第三步:击穿的分布式锁
击穿发生在热点 key 刚过期的一瞬间,成百请求同时未命中、一起回源。我们用 SET NX PX 做互斥重建,只让一个请求查库,其余请求短暂等待后重读缓存。释放锁必须用 Lua 脚本比对 token,不能直接 DEL。
第四步:雪崩的随机 TTL
雪崩是大量 key 在同一时刻集中过期。所有写入点统一加抖动,把 TTL 打散到基础值的百分之九十到一百一十,并在凌晨低峰预热 TOP 5000 商品。降级开关也提前准备好:Redis 异常时直接读库。同时用本地缓存限流,避免把数据库一次性压垮。
三处真实踩坑
第一,初始版本没设 socket_timeout,主备切换那半分钟里所有工作线程阻塞在建连上,接口全面超时,比没有缓存更糟。第二,锁 TTL 设成三秒而一次慢查询要八秒,锁提前释放导致并发重建,改到十秒并补索引后才稳定。第三,曾把一个十兆的评论列表塞进单个 key,传输时阻塞同一连接上的其他请求,拆成分页 key 才解决。键名也要提前规范成 业务:实体:id,否则多团队共用一个实例必然冲突。
结论
Redis 不是加了就快的银弹,真正决定稳定性的是超时预算、降级路径、key 规范和锁语义。把穿透、击穿、雪崩当成三个独立的工程问题逐个解决,缓存才会成为数据库前面的一道缓冲,而不是新的故障点。这也是我们复盘后形成的团队共识。
- 点赞
- 收藏
- 关注作者
评论(0)