ToolkitX
知识库工具箱

应用场景

缓存、计数器、排行榜、分布式锁

25min·进阶

01. 缓存模式——旁路缓存

旁路缓存(Cache Aside)最常用,流程直接: 查数据:先查 Redis → 有直接回 → 没有查数据库 → 写入 Redis 再回 写数据:先更新数据库 → 删掉 Redis 对应缓存(等下次查询自动重建) 这最稳妥,避免了缓存和数据库不一致的坑。
javascript
// Node.js 旁路缓存示例
async function getUser(id) {
const cacheKey = `user:${id}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);

const user = await db.query('SELECT * FROM users WHERE id = ?', [id]);
if (user) await redis.setex(cacheKey, 3600, JSON.stringify(user));
return user;
}

// 更新时删缓存
async function updateUser(id, data) {
await db.query('UPDATE users SET ? WHERE id = ?', [data, id]);
await redis.del(`user:${id}`);
}

02. 缓存穿透、击穿、雪崩——三大问题

这仨是面试必考、线上常见事故: 缓存穿透——查不存在的数据(如 ID=-1),缓存没有直接打到数据库。攻击者专门构造这种请求能打爆数据库。解决:布隆过滤器,或者查不到也缓存空值(短时间)。 缓存击穿——热点 key 刚好过期,瞬间大量请求涌向数据库。解决:加互斥锁,或者「永不过期」异步刷新。 缓存雪崩——大量 key 同时过期,数据库瞬间被压垮。解决:给过期时间加随机值,避免集中过期。
javascript
// 防穿透:缓存空值
let data = await redis.get(`product:${id}`);
if (data === 'NULL') return null;
if (data) return JSON.parse(data);
const product = await db.findById(id);
if (product) {
await redis.setex(`product:${id}`, 3600, JSON.stringify(product));
} else {
await redis.setex(`product:${id}`, 60, 'NULL');  // 空值也缓存
}
return product;

// 防雪崩:过期时间加随机
const ttl = 3600 + Math.floor(Math.random() * 600);
await redis.setex(key, ttl, value);
布隆过滤器防穿透神器——极小的内存判断「某 key 一定不存在」。Redis 4.0+ 有内置布隆过滤器模块。

03. 分布式锁

多台服务器抢同一资源需要分布式锁协调。Redis 的 SETNX 可做简易版,但生产建议用 Redlock 算法或 Redisson 等成熟库。
bash
# 简易分布式锁
SET lock:order:123 "random-value" NX EX 30

# 释放用 Lua 保证原子性(先查再删)
EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:order:123 random-value
简单 SETNX 锁在 Redis 主从切换时可能丢失(主挂了锁还没同步到从)。Redlock 通过在多个独立 Redis 实例同时加锁来解决。

04. 计数器与限流器

利用 Redis 单线程和原子自增轻松实现计数器和限流器。点赞数、阅读量——INCR 搞定。固定窗口限流——设 key 带过期,每次 INCR 超了就拒绝。滑动窗口限流——用 Sorted Set 记录时间戳,精准控制。
bash
# 固定窗口限流(简单版):1 分钟最多 100 次
INCR rate:api:127.0.0.1
EXPIRE rate:api:127.0.0.1 60

# 滑动窗口限流(精确版)——用 Sorted Set
ZADD rate:api:127.0.0.1 <timestamp> <unique-id>
ZREMRANGEBYSCORE rate:api:127.0.0.1 0 <timestamp减60秒>
ZCARD rate:api:127.0.0.1  # 这分钟内多少次

知识测验

1/5正确 0

缓存穿透指的是什么?

下一节

性能优化

下一节