跳到主要内容

Redis 缓存设计与一致性

· 阅读需 2 分钟
一介布衣
全栈开发者 / 技术写作者

Redis 到了 2021 年,已经不是“可选优化”了。很多 Node.js 服务一旦扛到真实流量,列表读取、热点详情、配置查询这些地方,很自然就会想到把 Redis 接进来。

但真正难的地方,不是怎么把数据塞进缓存,而是缓存和数据库怎么长期保持在一个可控边界里。

缓存最容易做成什么样

很多项目一开始都会这样:

  • 先查 Redis
  • 没有就查数据库
  • 查到后再回填 Redis

这套模式没有错,它就是最常见的 cache aside。但真正上线后麻烦会出在写路径。

写路径为什么决定缓存是否靠谱

如果文章更新后只写数据库,不删缓存,那旧数据就会一直在。

如果先删缓存再写数据库,读请求一拥而上,又可能把旧值重新写回缓存。

所以对大多数内容系统,我更常用的思路是:

  1. 先更新数据库
  2. 再删除缓存
async function updatePost(postId, payload) {
await PostModel.update(payload, {
where: { id: postId },
});

await redis.del(`post:${postId}`);
}

这不意味着它完美无缺,但在内容系统这类读多写少场景里,通常已经足够稳。

一致性不等于绝对实时

缓存设计最常见的误区,是追求“任何时刻都必须完全一致”。实际上,多数内容系统更在意的是:

  • 大部分时间读得快
  • 写后能尽快收敛
  • 错误成本可控

只要这三点成立,缓存就是有效的。

哪些数据适合缓存

我一般会优先缓存这些内容:

  • 文章详情
  • 首页推荐列表
  • 站点配置
  • 热点标签榜单

而一些强实时、强事务的管理动作,就不适合轻易缓存。

TTL 不是万能药

给缓存统一加 5 分钟过期时间很方便,但不能指望 TTL 解决所有一致性问题。TTL 更像兜底,不是主策略。真正重要的还是写路径怎么处理。

小结

Redis 在 2021 年已经是 Node.js 服务里非常基础的一环,但缓存设计真正考验的不是“会不会用”,而是有没有能力把一致性边界说清楚。边界清楚了,Redis 才会真正帮到系统,而不是制造更多隐患。