首页 / 知识库 / python服务端进阶 / 服务端高并发优化

03 - 缓存:高并发第一利器

缓存是高并发优化里性价比最高、见效最快的一招。核心思想只有一句话:把慢的、频繁访问的数据,放到快的地方,下次直接拿。

一句话先记住:缓存是用「空间」换「时间」,用「可能的不一致」换「极快的速度」。


3.1 为什么缓存这么快

不同存储介质的读取速度,差了好几个数量级:

CPU 缓存    :  ~1 纳秒
内存        :  ~100 纳秒       ← Redis 在这里
SSD 硬盘    :  ~100 微秒       ← 数据库数据大多在这
网络往返     :  ~500 微秒
机械硬盘     :  ~10 毫秒

从内存读,比从磁盘读快成百上千倍。缓存的本质,就是把数据从「慢介质」搬到「快介质」


3.2 多级缓存:层层拦截

缓存不是只有 Redis。一个请求从用户到数据库,沿途可以设多道缓存:

用户


[浏览器缓存]     ← 静态资源、HTTP 缓存头


[CDN 缓存]      ← 图片/视频/JS,就近返回


[网关缓存]       ← Nginx 缓存热点页面


[应用本地缓存]    ← 进程内内存(最快,但每台机器一份)


[分布式缓存 Redis] ← 多台应用共享,容量大
 │  没命中才查库

[PostgreSQL]
层级特点适合缓存什么
浏览器 / CDN离用户最近,最快静态资源、不常变的内容
本地缓存进程内,纳秒级,但每台一份、有一致性问题超高频的小数据(配置、字典)
Redis多机共享、容量大大部分热点数据(用户信息、商品详情)

本地缓存 + Redis 组合是常见的「二级缓存」:先查本地,本地没有再查 Redis,Redis 没有再查库。


3.3 缓存的基本模式:旁路缓存(Cache-Aside)

最常用的读写模式,你一定要理解它:

【读数据】
  1. 先查缓存
  2. 命中 → 直接返回                (快)
  3. 没命中 → 查数据库 → 写回缓存 → 返回

【写数据】
  1. 更新数据库
  2. 删除缓存(不是更新缓存!)

为什么写的时候是「删除缓存」而不是「更新缓存」?

  • 删除简单、不易出错;下次读的时候自然会重新加载最新值
  • 如果去更新缓存,两个并发写可能让缓存存进「旧值」,产生不一致

3.4 缓存一致性:最头疼的问题

数据库更新了,缓存还是旧的——这就是不一致。经典方案是「延迟双删」:

1. 删除缓存
2. 更新数据库
3. 稍等一会儿(比如 500ms)
4. 再删一次缓存   ← 删掉这期间可能被读进来的旧值

重要认知:缓存和数据库的强一致几乎做不到,我们追求的是「最终一致」。对一致性要求极高的数据(如账户余额),就别缓存,直接查库。


3.5 缓存三大经典坑

这三个问题几乎是面试必考,也是线上事故高发区。

坑一:缓存穿透 —— 查一个根本不存在的数据

恶意请求:查 id = -1 的用户(根本不存在)
  → 缓存没有(因为不存在)
  → 每次都穿透到数据库
  → 大量这种请求 → 数据库被打垮

对策

  • 把「不存在」也缓存起来(缓存一个空值,设短过期时间)
  • 布隆过滤器:先判断「这个 id 可能存在吗」,不可能存在的直接拦掉

坑二:缓存击穿 —— 一个热点 key 突然过期

某个超热门商品的缓存 22:00 过期
  → 这一瞬间成千上万请求同时发现「缓存没了」
  → 一起涌向数据库查同一条数据
  → 数据库瞬间被打崩

对策

  • 互斥锁:只让第一个请求去查库并重建缓存,其他请求等一下
  • 热点数据不过期(或后台异步续期)

坑三:缓存雪崩 —— 大量 key 同时过期

系统重启时,一批缓存设了相同的过期时间(比如都 1 小时)
  → 1 小时后,成千上万 key 同时失效
  → 所有请求同时打到数据库
  → 数据库崩溃 → 引发连锁反应(雪崩)

对策

  • 过期时间加随机值(比如 1 小时 ± 随机几分钟),错开失效时间
  • 缓存高可用(Redis 主从 / 集群),别让缓存整个挂掉
三大坑一句话区别核心对策
穿透不存在的数据缓存空值 / 布隆过滤器
击穿单个热点 key 失效互斥锁 / 热点不过期
雪崩大量 key 同时失效过期时间加随机 / 缓存高可用

3.6 缓存的代价(别滥用)

  • 一致性问题:缓存和数据库天然会有短暂不一致
  • 额外复杂度:多了一套要运维的 Redis,多了「命中率、过期、淘汰」要操心
  • 内存成本:内存比磁盘贵,不能什么都往里塞

什么该缓存:读多写少、能容忍短暂不一致、访问频繁的数据(用户信息、商品详情、配置)。 什么不该缓存:写极频繁、要求强一致、几乎不被重复读的数据。


本章小结

你要记住要点
为什么快内存比磁盘快成百上千倍
多级缓存浏览器 → CDN → 本地 → Redis → DB,层层拦截
读写模式旁路缓存:读时回填,写时删除缓存
一致性追求最终一致,延迟双删;强一致数据别缓存
三大坑穿透(查不存在)、击穿(单热点失效)、雪崩(大量同时失效)

下一章预告:缓存解决了「读」的压力,那「写」和「慢任务」怎么办?答案是异步化 + 消息队列削峰。


上一章 ← 02 - 全链路瓶颈地图 | 下一章 → 04 - 异步化与削峰