Skip to content

使用缓存提升性能

前面几篇文章主要解决数据库的扩展问题:

  • 索引优化(提升查询效率)
  • 主从复制(分离读写)
  • 分库分表(提升存储和写入能力)

但即使做完这些优化,系统仍然会遇到一个现实问题:

数据库的处理能力永远比不上内存。

例如:

text
Redis:几十万 QPS
MySQL:几千 ~ 几万 QPS

当系统的大部分请求都在「读数据」时,数据库很快就会成为瓶颈。

因此互联网系统普遍采用的方案是:

用缓存承担绝大部分查询请求,让数据库只负责少量请求。

一、为什么缓存能提升性能

本质原因只有一个:

内存远快于磁盘。

数据库查询过程:

text
查询

索引查找

磁盘IO

返回结果

即使命中 Buffer Pool:

也需要数据库参与。

而缓存:

text
查询

Redis

返回

直接从内存读取。

延迟通常:

text
MySQL
毫秒级(ms)

Redis
微秒级(us)

相差几十倍甚至上百倍。

因此:

缓存的核心价值就是:

把大量读请求挡在数据库之前。

二、缓存适合解决什么问题

缓存并不是存所有数据。

最适合缓存的是:

热点数据

例如:

  • 商品详情
  • 用户信息
  • 首页配置
  • 热门文章

特点:

text
读多写少
重复访问

例如:

100万人访问同一个商品。

数据库实际上只需要查一次。

后面:

text
Redis

返回

即可。

三、缓存架构是怎样的

最经典架构:

text
      用户请求


        Redis
      /       \
 命中         未命中
  │             │
  ▼             ▼
返回结果     MySQL


          写入缓存


             返回

这就是:

Cache Aside Pattern(旁路缓存模式)

也是互联网公司最主流方案。

四、缓存命中率为什么重要

缓存最大的价值来自:

命中率。

例如:

系统:

text
10000 QPS

缓存命中率:

text
90%

则:

数据库只需要承担:

text
1000 QPS

如果命中率:

text
99%

数据库只承担:

text
100 QPS

所以:

缓存系统最核心指标其实不是 QPS。

而是:

Cache Hit Ratio(缓存命中率)。

五、缓存更新的两种方式

文章重点讨论:

数据变化后怎么办?

方案一:更新缓存

流程:

text
更新数据库

更新缓存

看起来合理。

但实际问题很多:

数据库成功

缓存失败

导致:

text
数据库
新数据

缓存
旧数据

出现不一致。

方案二:删除缓存(推荐)

流程:

text
更新数据库

删除缓存

后续请求:

text
缓存未命中

查询数据库

重建缓存

这就是互联网最常见方案。

核心原因:

删除缓存比更新缓存简单得多。

六、缓存会带来哪些问题

文章强调:

缓存不是银弹。

缓存解决数据库压力问题,

同时引入新的复杂性。

七、缓存穿透

问题:

查询一个根本不存在的数据。

例如:

text
id=-1

流程:

text
Redis
没有

MySQL
没有

返回空

下次再查:

依然:

text
Redis
没有

MySQL
没有

数据库被持续攻击。

解决方案:

1. 缓存空对象

例如:

text
NULL

也缓存。

2. 布隆过滤器

先判断:

text
数据是否可能存在

不存在直接拦截。

八、缓存击穿

问题:

某个热点 Key 失效。

例如:

text
商品详情

同时:

text
10000请求

到来。

结果:

全部穿透到数据库。

数据库瞬间崩溃。

解决方案:

互斥锁

第一个线程:

text
查询数据库
重建缓存

其他线程等待。

本质:

同时只允许一个线程回源数据库。

九、缓存雪崩

问题:

大量缓存同时失效。

例如:

text
凌晨0点
100万缓存同时过期

结果:

所有请求打到数据库。

数据库崩溃。

解决方案:

过期时间随机化

例如:

text
3600秒 ± 随机数

避免同时过期。

多级缓存

text
本地缓存

Redis

MySQL

限流降级

保护数据库。

十、缓存与数据库一致性

这是缓存最难的问题。

文章强调:

缓存与数据库不可能绝对实时一致。

互联网系统追求的是:

最终一致性。

典型做法:

text
更新数据库

删除缓存

之后:

新的查询自动重建缓存。

虽然短时间:

可能读到旧数据。

但最终会一致。

十一、缓存使用原则

文章总结出几个核心原则。

1. 缓存热点数据

不要缓存全部数据。

否则:

  • 内存成本太高
  • 命中率反而下降

2. 缓存读多写少数据

例如:

text
商品详情
用户信息

不适合:

text
账户余额
库存实时数据

频繁更新的数据。

3. 缓存是加速层

不是数据源。

真正的数据源永远是:

text
MySQL

4. 优先保证数据库正确

缓存可以丢。

数据库不能错。

十二、本章核心脉络

系统演进过程:

text
全部查询MySQL

数据库压力增大

引入Redis缓存

命中率提升

数据库压力下降

出现缓存问题

穿透
击穿
雪崩
一致性

形成完整缓存体系

十三、本章最重要的理解

前面的:

  • 索引
  • 主从
  • 分库分表

本质上都在优化数据库。

而缓存不一样。

缓存的思想是:

既然数据库扛不住,那就尽量不要让请求到数据库。

因此:

数据库优化属于:

text
提高数据库能力

而缓存属于:

text
减少数据库工作量

从效果来看:

缓存往往比数据库优化带来的收益更大。

很多互联网系统:

text
95%以上请求
根本不会到数据库

都是缓存直接返回。

一句话总结

缓存的核心价值是利用内存的高速访问能力,将绝大多数读请求拦截在数据库之前,从而显著提升系统性能和吞吐量。实际工程中通常采用 Cache Aside 模式,并重点解决缓存穿透、击穿、雪崩以及数据一致性问题,最终实现“数据库存储数据,缓存承担流量”的架构目标。

Released under the MIT License.