缓存架构
缓存是存储数据子集的高速存储层,访问速度比主存储快。缓存是提升系统性能、降低数据库负载、提高响应速度的有效手段。
缓存原理
局部性原理
局部性原理是缓存的理论基础。时间局部性:最近访问的数据很可能再次被访问。空间局部性:最近访问数据附近的数据很可能被访问。
缓存的本质是用空间换时间,通过存储热点数据,减少访问慢速存储(数据库、磁盘)的次数。
缓存命中率
缓存命中率 = 缓存命中次数 / 总访问次数。命中率越高,缓存效果越好。缓存命中率取决于:数据分布(是否集中热点)、缓存容量(能否存储热点)、淘汰策略(是否保留热点)、预取策略(是否提前加载)。
缓存模式
Cache-Aside(旁路缓存)
Cache-Aside 是最常用的缓存模式。读:先读缓存,缓存未命中则读数据库,然后写入缓存。写:先更新数据库,然后删除缓存(或更新缓存)。
Cache-Aside 的问题:缓存未命中时有缓存击穿风险(大量请求同时访问不存在的 key)。解决方案:布隆过滤器(快速判断 key 是否存在)、缓存空值(防止重复查询数据库)。
Read-Through(读穿透)
Read-Through 是缓存层负责从数据库加载数据。读:先读缓存,缓存未命中则缓存层读数据库并写入缓存。应用只与缓存交互,不直接访问数据库。
Read-Through 的好处:简化应用逻辑,缓存层统一管理数据加载。Read-Through 的代价:缓存层需要实现数据库访问逻辑。
Write-Through(写穿透)
Write-Through 是写数据时同时更新缓存和数据库。写:先更新数据库,然后更新缓存。缓存和数据库保持一致。
Write-Through 的好处:数据一致性强,缓存始终是最新的。Write-Through 的代价:写操作延迟高(需要同时更新缓存和数据库)。
Write-Behind(异步写)
Write-Behind 是写数据时只更新缓存,异步批量更新数据库。写:更新缓存,缓存层异步批量更新数据库。
Write-Behind 的好处:写操作延迟低,缓存层可以合并写操作。Write-Behind 的代价:数据可能丢失(缓存层崩溃时)、一致性弱(缓存和数据库可能不一致)。
缓存淘汰策略
LRU(Least Recently Used)
LRU 淘汰最近最少使用的数据。LRU 的实现:哈希表 + 双向链表。哈希表提供 O(1) 查找,双向链表记录访问顺序。
LRU 的问题:实现复杂(需要维护双向链表)、内存占用大(每个缓存项需要前驱和后继指针)。LRU 的改进:LRU-K(考虑最近 K 次访问)、Two Queues(二级 LRU)。
LFU(Least Frequently Used)
LFU 淘汰访问频率最低的数据。LFU 的实现:哈希表 + 优先队列(最小堆)。优先队列按访问频率排序,淘汰频率最低的数据。
LFU 的问题:新数据可能被淘汰(访问频率低但可能是热点)、实现复杂(需要维护优先队列)。LFU 的改进:LFU with Dynamic Aging(考虑时间因素,降低旧数据的频率)。
FIFO(First In First Out)
FIFO 淘汰最先加入的数据。FIFO 的实现:队列。FIFO 的问题:可能淘汰热点数据(热点数据是最先加入的)、不考虑访问频率。
Random(随机淘汰)
Random 随机淘汰数据。Random 的好处:实现简单、无额外开销。Random 的代价:可能淘汰热点数据、缓存命中率低。
缓存问题
缓存穿透
缓存穿透是查询不存在的数据,缓存和数据库都没有,导致每次请求都穿透到数据库。解决方案:布隆过滤器(快速判断 key 是否不存在)、缓存空值(缓存不存在的 key)、限流(防止恶意攻击)。
缓存击穿
缓存击穿是热点 key 过期,大量请求同时查询该 key,导致数据库压力突增。解决方案:加锁(只允许一个请求查询数据库)、热点 key 不过期(延长过期时间)、互斥锁(分布式锁)。
缓存雪崩
缓存雪崩是大量 key 同时过期,导致大量请求穿透到数据库。解决方案:过期时间加随机值(避免同时过期)、多级缓存(本地缓存 + 分布式缓存)、缓存高可用(集群部署)。
缓存一致性
一致性要求
强一致性:缓存和数据库的数据始终一致。最终一致性:缓存和数据库的数据最终一致,允许短暂不一致。弱一致性:不保证数据一致。
一致性方案
更新数据库后更新缓存:可能不一致(更新缓存失败)、延迟高(需要等待缓存更新)。更新数据库后删除缓存:可能不一致(删除缓存前有读请求)、延迟低。
延迟双删:先删除缓存,更新数据库,延迟后再删除缓存。延迟双删可以减少不一致窗口期。
监听 Binlog:通过监听数据库的 Binlog,异步更新缓存。监听 Binlog 可以保证最终一致性,但实现复杂(需要 Canal、Debezium 等工具)。
缓存类型
本地缓存
本地缓存是应用进程内的缓存,如 Guava、Caffeine、Ehcache。本地缓存的优势:速度快(无网络开销)、简单(无需额外部署)。本地缓存的问题:容量有限(受限于进程内存)、多实例不一致(每个实例的缓存独立)。
分布式缓存
分布式缓存是独立的缓存服务,如 Redis、Memcached。分布式缓存的优势:容量大(可扩展)、多实例共享(所有实例访问同一缓存)。分布式缓存的问题:网络开销、序列化开销、单点故障(需要集群)。
多级缓存
多级缓存是本地缓存 + 分布式缓存的组合。读:先读本地缓存,未命中则读分布式缓存,再未命中则读数据库。写:更新数据库,删除分布式缓存,删除本地缓存。
多级缓存的好处:减少网络开销(本地缓存命中率高)、降低分布式缓存压力。多级缓存的问题:一致性复杂(多级缓存同步)、本地缓存占用内存。
缓存应用
页面缓存
缓存渲染后的 HTML 页面,减少数据库查询和页面渲染。适用场景:静态页面、访问量大的首页、新闻列表。
对象缓存
缓存查询结果(如用户信息、商品信息),减少数据库查询。适用场景:热点数据、读多写少的数据。
查询缓存
缓存数据库查询结果(如 SELECT 结果),减少数据库查询。适用场景:复杂查询、频繁查询。
缓存是提升系统性能的有效手段,但也会引入一致性问题。理解缓存的模式、淘汰策略、一致性问题,有助于设计合适的缓存架构。