应用怎样使用 Redis 缓存¶
缓存是为了让重复查询更便宜。商品价格以 MySQL 为准,Redis 保存一份暂时可用的结果;多久允许旧数据、Redis 故障时怎么办,需要应用与运维一起约定。
一次正常查询¶
这是常见的 Cache Aside(旁路缓存)。Redis 不会自己回源 MySQL。刚重启、刚扩容或清空缓存时,大量请求未命中,数据库压力会瞬间升高,所以不能把“所有缓存随时可清空”理解成对业务没有影响。
商品改价以后,缓存怎么更新¶
常见做法是先提交 MySQL,再删除对应缓存。下次查询重新加载新值。选择删除而不是每次直接覆盖,是为了减少多个更新请求把旧值覆盖回缓存的机会,也避免更新没人读取的数据。
但这仍不是严格一致性:
此时缓存会暂时陈旧。可根据业务使用较短 TTL、带版本的更新控制、可靠的失效通知与重试、CDC 等机制缩小窗口。“延迟再删一次”也有时序假设,不是通用一致性保证。
支付确认等关键读取可直接访问权威数据源并使用正确事务规则。页面展示允许短暂旧数据时,才按容忍时间设计缓存。运维重点监控失效失败、同步积压和异常回源,不能仅凭 Redis 存活就判断价格正确。
穿透、击穿、雪崩,用现象记¶
| 名称 | 请求发生了什么 | 常见处理 |
|---|---|---|
| 穿透 | 查询根本不存在的商品,每次都绕过缓存去数据库 | 校验输入、短期缓存空结果;规模大时评估 Bloom Filter |
| 击穿 | 一个特别热门的 key 刚过期,很多请求同时回源 | 合并同 key 的重建请求、提前刷新,或按业务允许返回旧值 |
| 雪崩 | 大量 key 同时失效,或整个 Redis 不可用,数据库被冲击 | TTL 加随机抖动、分批预热、回源限流、高可用与降级 |
Bloom Filter 用于快速判断“肯定不存在”或“可能存在”;可能存在仍需查询。过滤器的数据同步与新商品写入流程要配套,不能把更新遗漏变成业务漏查。空结果也要短 TTL,否则商品刚创建仍可能被判不存在。
给过期时间加随机数,例如基础 300 秒再加 0~60 秒抖动,只能错开一部分集中失效,不能解决 Redis 整体故障。降级也应限制回源流量,不能让所有请求无上限直冲数据库。
缓存之外,几个容易理解的用途¶
登录会话。 多个应用实例共享会话状态,不必把用户固定在一台机器。会话丢失可能要求重新登录,需要与商品缓存分开考虑淘汰策略。
计数与限流。 用 INCR 记录请求次数,但“自增 + 首次设置 TTL”需要原子组合,否则可能留下不过期计数。固定窗口在边界可能允许短时双倍突发,更精细需求再考虑滑动窗口或令牌桶。
防重复处理。 SET ... NX EX ... 可表示“仅在标记不存在时创建”。不过标记成功后业务失败、或 Redis 切换丢失标记,都需要恢复逻辑。真正影响订单的重复处理仍应有数据库唯一约束或幂等记录兜底。
分布式锁先理解租约¶
锁的基本思路是让一个请求获得带有效期的占用标记,其他请求暂时等待:
返回 OK 表示拿到这个 10 秒租约,返回空表示没拿到。token 必须每次唯一;释放时要原子比较 token 再删除,不能直接 DEL,否则锁过期被其他请求拿到后,会误删别人的锁。
如果任务运行超过 10 秒,原持有者可能还在工作,其他请求已经拿到新锁。续租、暂停、网络分区和主从切换都影响正确性。关键资源可进一步使用 fencing token,由资源端拒绝过期持有者的操作;需要严格互斥时应选择与业务一致性要求匹配的协调方式。不要把这一条 SET 当成完整的生产锁实现。
锁的有效期、释放与复制风险可继续参考 Redis 分布式锁说明。
该看哪些指标¶
缓存命中率可以用时间窗口内 keyspace_hits / (keyspace_hits + keyspace_misses) 估算,但它是 Redis 查找统计,未必等于某个业务接口的命中率。需要结合应用缓存指标、回源 QPS、数据库负载、TTL 分布和错误率。
高命中率也可能缓存的是过期业务内容。监控回答“快不快”,业务校验回答“对不对”,两者都需要。
下一步:持久化与内存管理。