第9章

🚀 缓存架构

掌握缓存策略、本地缓存、分布式缓存、Redis集群和缓存一致性等核心技术

学习目标

缓存概述

缓存(Cache)是一种存储技术,用于临时存储经常访问的数据,以提高系统性能和响应速度。在系统架构中,缓存是提升性能的重要手段,能够显著减少数据库访问压力,提高用户体验。

核心理解

缓存的本质是用空间换时间,通过在高速存储介质中保存热点数据,避免重复的耗时操作。

缓存的层次结构

CPU缓存
L1、L2、L3缓存,存储CPU频繁访问的指令和数据,访问速度最快。
内存缓存
应用程序级别的缓存,如本地缓存、JVM堆内缓存等。
分布式缓存
跨多个节点的缓存系统,如Redis、Memcached等。
磁盘缓存
操作系统级别的磁盘缓存,提高文件系统访问性能。

缓存的优势

缓存策略

缓存策略决定了数据如何被缓存、更新和淘汰。选择合适的缓存策略对系统性能和数据一致性至关重要。

读取策略

Cache-Aside
应用程序负责维护缓存,先查缓存,缓存未命中时查数据库并更新缓存。
Read-Through
缓存系统负责从数据源加载数据,应用程序只需要从缓存读取。
Refresh-Ahead
在缓存过期前主动刷新数据,确保热点数据始终可用。

写入策略

Write-Through
同时写入缓存和数据库,保证数据一致性但性能较低。
Write-Behind
先写缓存,异步写入数据库,性能高但可能丢失数据。
Write-Around
直接写入数据库,不更新缓存,适合写多读少的场景。

淘汰策略

常见淘汰算法
  • LRU (Least Recently Used):淘汰最近最少使用的数据
  • LFU (Least Frequently Used):淘汰使用频率最低的数据
  • FIFO (First In First Out):先进先出,淘汰最早进入的数据
  • TTL (Time To Live):基于过期时间淘汰数据
  • Random:随机淘汰数据,实现简单但效果一般

本地缓存

本地缓存是指在应用程序内部实现的缓存,数据存储在应用程序的内存空间中。本地缓存访问速度快,但受限于单机内存大小。

本地缓存的特点

高性能
无网络开销,访问速度极快,延迟通常在微秒级别。
高可靠
不依赖外部系统,避免网络故障和缓存服务器宕机。
低成本
无需额外的缓存服务器,降低系统复杂度和运维成本。
容量限制
受限于单机内存,无法存储大量数据,存在数据一致性问题。

常见的本地缓存实现

// Java中的本地缓存示例

// 1. 使用ConcurrentHashMap实现简单缓存
public class SimpleCache<K, V> {
    private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
    
    public V get(K key) {
        return cache.get(key);
    }
    
    public void put(K key, V value) {
        cache.put(key, value);
    }
}

// 2. 使用Caffeine实现高性能缓存
Cache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(10000)
    .expireAfterWrite(Duration.ofMinutes(10))
    .build();

// 3. 使用Guava Cache
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .expireAfterAccess(30, TimeUnit.MINUTES)
    .build(new CacheLoader<String, String>() {
        public String load(String key) {
            return loadFromDatabase(key);
        }
    });

分布式缓存

分布式缓存是部署在多个节点上的缓存系统,能够提供更大的存储容量和更高的可用性。Redis和Memcached是最常用的分布式缓存解决方案。

分布式缓存的优势

可扩展性
可以通过增加节点来扩展存储容量和处理能力。
数据共享
多个应用实例可以共享同一份缓存数据。
高可用性
通过主从复制和集群部署提供高可用性。
持久化
支持数据持久化,避免缓存重启后数据丢失。

Redis vs Memcached

技术对比
特性 Redis Memcached
数据类型 丰富的数据类型 仅支持字符串
持久化 支持RDB和AOF 不支持
集群 原生集群支持 客户端分片
性能 单线程,CPU密集 多线程,内存密集

Redis集群

Redis集群是Redis的分布式解决方案,通过数据分片和主从复制提供高可用性和可扩展性。

集群架构模式

主从复制
一个主节点配合多个从节点,提供读写分离和故障转移。
哨兵模式
通过哨兵节点监控主从状态,自动进行故障转移。
集群模式
多个主节点组成集群,数据自动分片,支持水平扩展。

数据分片策略

# Redis集群配置示例

# 1. 创建集群节点配置
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes

# 2. 启动集群节点
redis-server redis-7000.conf
redis-server redis-7001.conf
redis-server redis-7002.conf

# 3. 创建集群
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
  --cluster-replicas 1

# 4. 查看集群状态
redis-cli -c -p 7000 cluster nodes
集群分片原理

Redis集群使用一致性哈希算法将16384个槽位分配给不同的主节点,每个key通过CRC16算法计算出对应的槽位,然后路由到相应的节点。

缓存一致性

缓存一致性是指缓存中的数据与数据库中的数据保持同步。这是缓存系统设计中的核心挑战之一。

一致性问题的产生

时间差异
缓存更新和数据库更新之间存在时间差,导致数据不一致。
操作失败
缓存或数据库操作失败,导致部分数据更新成功,部分失败。
并发访问
多个线程同时读写缓存和数据库,可能导致数据竞争。

解决方案

Cache-Aside + 删除
更新数据库后删除缓存,下次读取时重新加载最新数据。
双写一致性
同时更新数据库和缓存,使用分布式锁保证原子性。
异步更新
通过消息队列异步更新缓存,保证最终一致性。
Canal同步
监听数据库binlog,实时同步数据变更到缓存。

最佳实践

实践建议
  • 优先删除缓存:更新数据时优先删除缓存,避免脏数据
  • 设置过期时间:为缓存数据设置合理的TTL,兜底保证一致性
  • 延迟双删:删除缓存后延迟一段时间再次删除,处理并发问题
  • 分布式锁:对于强一致性要求的场景,使用分布式锁
  • 监控告警:建立缓存命中率和一致性监控机制

缓存设计实践

在实际项目中,缓存的设计需要考虑业务特点、性能要求、一致性需求等多个因素。

缓存设计原则

热点数据优先
优先缓存访问频率高的热点数据,提高缓存命中率。
多级缓存
结合本地缓存和分布式缓存,构建多级缓存体系。
缓存保护
防止缓存穿透、击穿和雪崩等问题。
性能监控
建立完善的缓存性能监控和告警机制。

常见问题及解决方案

缓存问题处理
  • 缓存穿透:查询不存在的数据,使用布隆过滤器或缓存空值
  • 缓存击穿:热点数据过期,使用互斥锁或永不过期策略
  • 缓存雪崩:大量缓存同时失效,使用随机过期时间
  • 热点数据倾斜:某些数据访问过于集中,使用本地缓存分担压力
  • 内存溢出:缓存数据过多,合理设置淘汰策略和内存限制
上一章:消息队列 返回目录 下一章:监控与运维