跳转至

Redis

一句话:Redis 在高并发系统里同时扮演"缓存"与"协调器"(分布式锁)两个角色,是降低数据库压力、保证分布式一致性的核心中间件。

概念

Redis 是基于内存的、单线程(核心命令)的、基于 IO 多路复用的 KV 存储,单实例 QPS 可达十万级。它在后端系统承担两类职责:缓存(挡住读流量、降低 RT、保护 DB)与分布式协调(分布式锁、限流计数、排行榜)。

缓存引入后立刻带来新问题:缓存与数据库一致性缓存击穿/穿透/雪崩热点 key。分布式锁带来的是锁的正确性争议(看门狗续期、Redlock 的安全性证明)。这些问题没有银弹,需要根据业务对一致性与性能的要求做权衡,这正是资深工程师的价值所在。

原理

分布式锁:从 setnx 到 Redisson

最朴素的分布式锁用 SETNX(Set if Not eXists)实现,但存在一系列正确性问题:

问题 成因 解法
锁非原子 SETNX + EXPIRE 两步,中间宕机导致锁永不释放 SET key value NX EX 30 原子设值+过期
误删他人锁 A 持锁超时自动释放,B 抢到锁,A 执行完误删 B 的锁 value 存唯一标识,删除前 Lua 脚本校验
锁过期仍执行 业务执行超时,锁已释放但业务还在跑 看门狗(watchdog)自动续期
主从故障丢锁 master 加锁后未同步到 slave 就宕机,切换后锁丢失 Redlock(多实例过半数)

Redisson 看门狗机制:Redisson 的 RLock 加锁时若不显式指定 leaseTime,默认 30 秒并启动一个后台线程(看门狗),每隔 10 秒(lockWatchdogTimeout/3)检查持锁线程是否还活着,活着就把过期时间重置回 30 秒,从而避免"业务没执行完锁就过期"。看门狗本质是一个定时续期的定时任务,依赖持有锁的客户端进程存活。

Redlock 争议

Redlock 是 antirez 提出的多 Redis 实例算法:向 N(通常 5)个独立 master 同时申请锁,超过半数(N/2+1)成功且耗时小于锁有效期才算加锁成功。目的是解决单点主从切换丢锁的问题。

Martin Kleppmann 在著名文章中批评 Redlock:依赖时钟同步,在 GC pause、进程暂停、时钟漂移下,仍可能出问题,对"需要正确性保证"的场景(如金融扣款)应使用基于共识(Zookeeper/etcd)的锁,Redis 锁只适合"对偶尔出错容忍、性能优先"的场景。这一争议是面试深水区。

缓存一致性

缓存与 DB 双写如何保证一致?常见策略对比:

策略 做法 问题
先更新 DB 再删缓存 写 DB → DEL cache 删除失败则不一致(可重试/MQ 兜底)
先删缓存再更新 DB DEL cache → 写 DB 并发下:A 删缓存 → B 查 DB 旧值写回缓存 → A 更新 DB,长期不一致
延迟双删 删缓存 → 写 DB → 延迟 N 秒再删一次缓存 修补"先删后写"的并发窗口,延迟时间难精确
Canal 监听 binlog DB 变更 → Canal 解析 binlog → 异步删缓存 最终一致、解耦、一致性最稳,但引入组件与延迟

工程上推荐 "更新 DB + 删除缓存 + Canal 兜底":写时先删缓存再更新 DB 并配合短延迟双删,同时用 Canal 监听 binlog 异步删除缓存做最终一致性兜底。

多级缓存

为扛高并发读,构建多级缓存:

层级 介质 特点
L1 本地缓存 Caffeine/Guava 进程内,纳秒级,无网络开销,但多机不一致
L2 分布式缓存 Redis 跨机共享,毫秒级
DB MySQL 真相源

L1+L2 配合:先查本地,未命中查 Redis,再未命中查 DB 并回写。本地缓存适合"读多写少、可容忍秒级不一致"的数据(配置、字典)。

缓存三大问题与热点 key

问题 成因 解法
缓存穿透 查询不存在的数据,每次打到 DB 缓存空值、布隆过滤器
缓存击穿 热点 key 过期瞬间,大量请求打到 DB 互斥锁重建、热点 key 永不过期+异步刷新
缓存雪崩 大量 key 同时过期或 Redis 宕机 过期时间加随机偏移、多级缓存、Redis 集群高可用
热点 key 单 key 流量集中打爆单分片 本地缓存、key 分片打散(多副本)、读写分离到 slave

实战要点

结合 Redis 在交易链路与 Agent 平台(Redis 分布式锁/缓存、99.99% 可用)的经验:

  1. 分布式锁务必用 Redisson 而非手撸 setnx:Redisson 封装了原子加锁、看门狗续期、Lua 释放、可重入,避免手写的各种坑。手写 setnx 在"误删他人锁"和"锁过期业务仍在跑"上几乎必然踩坑。

  2. 看门狗不是万能,业务必须有超时兜底:看门狗依赖客户端进程存活,若进程假死(GC、死循环)会一直续期造成死锁。关键资源加锁必须设最大持有时间与业务超时,配合死锁检测告警。

  3. 缓存一致性优先"删缓存"而非"更新缓存":删除是幂等的、不依赖计算;更新缓存有并发覆盖风险。结合 Canal binlog 兜底是高一致性场景的稳妥方案。

  4. 热点 key 在大促前预识别:用离线分析+实时统计找出 TopN 热点 key(如爆款商品),提前做本地缓存预热与 key 分片打散,避免单分片被打挂。

  5. 金融/资金类锁不上 Redis:分布式锁的正确性要求高的场景(扣款、库存扣减),Redlock 仍有争议,应走数据库行锁、TCC 或 Zookeeper/etcd 共识锁。Redis 锁用于"防重复提交、限流、秒杀资格预筛"等容忍偶尔出错的场景。

  6. 多级缓存要设容量上限与淘汰策略:Caffeine 设 maximumSizeexpireAfterWrite,防止本地缓存无界膨胀导致 OOM。

本节相关题目

难度 题目 链接
基础 Redis 分布式锁实现、setnx 问题 → 题库
进阶 Redis 看门狗原理、Redlock 争议 → 题库
进阶 缓存一致性:延迟双删 vs Canal binlog → 题库