使用缓存提升性能
前面几篇文章主要解决数据库的扩展问题:
- 索引优化(提升查询效率)
- 主从复制(分离读写)
- 分库分表(提升存储和写入能力)
但即使做完这些优化,系统仍然会遇到一个现实问题:
数据库的处理能力永远比不上内存。
例如:
Redis:几十万 QPS
MySQL:几千 ~ 几万 QPS当系统的大部分请求都在「读数据」时,数据库很快就会成为瓶颈。
因此互联网系统普遍采用的方案是:
用缓存承担绝大部分查询请求,让数据库只负责少量请求。
一、为什么缓存能提升性能
本质原因只有一个:
内存远快于磁盘。
数据库查询过程:
查询
↓
索引查找
↓
磁盘IO
↓
返回结果即使命中 Buffer Pool:
也需要数据库参与。
而缓存:
查询
↓
Redis
↓
返回直接从内存读取。
延迟通常:
MySQL
毫秒级(ms)
Redis
微秒级(us)相差几十倍甚至上百倍。
因此:
缓存的核心价值就是:
把大量读请求挡在数据库之前。
二、缓存适合解决什么问题
缓存并不是存所有数据。
最适合缓存的是:
热点数据
例如:
- 商品详情
- 用户信息
- 首页配置
- 热门文章
特点:
读多写少
重复访问例如:
100万人访问同一个商品。
数据库实际上只需要查一次。
后面:
Redis
↓
返回即可。
三、缓存架构是怎样的
最经典架构:
用户请求
│
▼
Redis
/ \
命中 未命中
│ │
▼ ▼
返回结果 MySQL
│
▼
写入缓存
│
▼
返回这就是:
Cache Aside Pattern(旁路缓存模式)
也是互联网公司最主流方案。
四、缓存命中率为什么重要
缓存最大的价值来自:
命中率。
例如:
系统:
10000 QPS缓存命中率:
90%则:
数据库只需要承担:
1000 QPS如果命中率:
99%数据库只承担:
100 QPS所以:
缓存系统最核心指标其实不是 QPS。
而是:
Cache Hit Ratio(缓存命中率)。
五、缓存更新的两种方式
文章重点讨论:
数据变化后怎么办?
方案一:更新缓存
流程:
更新数据库
↓
更新缓存看起来合理。
但实际问题很多:
数据库成功
缓存失败
导致:
数据库
新数据
缓存
旧数据出现不一致。
方案二:删除缓存(推荐)
流程:
更新数据库
↓
删除缓存后续请求:
缓存未命中
↓
查询数据库
↓
重建缓存这就是互联网最常见方案。
核心原因:
删除缓存比更新缓存简单得多。
六、缓存会带来哪些问题
文章强调:
缓存不是银弹。
缓存解决数据库压力问题,
同时引入新的复杂性。
七、缓存穿透
问题:
查询一个根本不存在的数据。
例如:
id=-1流程:
Redis
没有
↓
MySQL
没有
↓
返回空下次再查:
依然:
Redis
没有
MySQL
没有数据库被持续攻击。
解决方案:
1. 缓存空对象
例如:
NULL也缓存。
2. 布隆过滤器
先判断:
数据是否可能存在不存在直接拦截。
八、缓存击穿
问题:
某个热点 Key 失效。
例如:
商品详情同时:
10000请求到来。
结果:
全部穿透到数据库。
数据库瞬间崩溃。
解决方案:
互斥锁
第一个线程:
查询数据库
重建缓存其他线程等待。
本质:
同时只允许一个线程回源数据库。
九、缓存雪崩
问题:
大量缓存同时失效。
例如:
凌晨0点
100万缓存同时过期结果:
所有请求打到数据库。
数据库崩溃。
解决方案:
过期时间随机化
例如:
3600秒 ± 随机数避免同时过期。
多级缓存
本地缓存
↓
Redis
↓
MySQL限流降级
保护数据库。
十、缓存与数据库一致性
这是缓存最难的问题。
文章强调:
缓存与数据库不可能绝对实时一致。
互联网系统追求的是:
最终一致性。
典型做法:
更新数据库
↓
删除缓存之后:
新的查询自动重建缓存。
虽然短时间:
可能读到旧数据。
但最终会一致。
十一、缓存使用原则
文章总结出几个核心原则。
1. 缓存热点数据
不要缓存全部数据。
否则:
- 内存成本太高
- 命中率反而下降
2. 缓存读多写少数据
例如:
商品详情
用户信息不适合:
账户余额
库存实时数据频繁更新的数据。
3. 缓存是加速层
不是数据源。
真正的数据源永远是:
MySQL4. 优先保证数据库正确
缓存可以丢。
数据库不能错。
十二、本章核心脉络
系统演进过程:
全部查询MySQL
↓
数据库压力增大
↓
引入Redis缓存
↓
命中率提升
↓
数据库压力下降
↓
出现缓存问题
↓
穿透
击穿
雪崩
一致性
↓
形成完整缓存体系十三、本章最重要的理解
前面的:
- 索引
- 主从
- 分库分表
本质上都在优化数据库。
而缓存不一样。
缓存的思想是:
既然数据库扛不住,那就尽量不要让请求到数据库。
因此:
数据库优化属于:
提高数据库能力而缓存属于:
减少数据库工作量从效果来看:
缓存往往比数据库优化带来的收益更大。
很多互联网系统:
95%以上请求
根本不会到数据库都是缓存直接返回。
一句话总结
缓存的核心价值是利用内存的高速访问能力,将绝大多数读请求拦截在数据库之前,从而显著提升系统性能和吞吐量。实际工程中通常采用 Cache Aside 模式,并重点解决缓存穿透、击穿、雪崩以及数据一致性问题,最终实现“数据库存储数据,缓存承担流量”的架构目标。