2-2-3-1-分布式缓存

1、分布式缓存总结与核心内容

分布式缓存是高并发系统的核心组件,用于缓解数据库压力、提升读取性能。其考察内容既覆盖底层原理(如Redis持久化、内存管理),也涉及工程实践(如一致性解决、热点处理),更关联分布式系统设计(如分片、容错)。以下是结构化的考察点拆解,结合原理、实践与面试追问:

一、分布式缓存基础概念与定位

1. 核心价值

  • 性能提升:缓存命中率可达80%-99%,将数据库QPS从1万降至100以内;
  • 压力分流:避免数据库成为“单点瓶颈”;
  • 用户体验:响应时间从百ms级降至ms级(如Redis读取约100μs)。

2. 定位边界

  • 适合缓存:高频读、低频写、非强一致性数据(如商品详情、用户信息);
  • 不适合缓存:频繁写(如实时计数器)、强一致性(如银行转账)、大key(如100MB的视频)。

二、常用分布式缓存产品对比

面试常考选型依据,需明确不同产品的优缺点与适用场景:

维度 Redis Memcached Ehcache
数据结构 支持String/Hash/List/Set/SortedSet等 仅字符串 支持多种(类似Redis,但本地)
分布式模型 主从/哨兵/Cluster(分布式) 客户端分片 本地缓存(可结合Terracotta做分布式)
持久化 RDB+AOF(支持混合) 支持磁盘持久化
内存管理 内置LRU/LFU/TTL淘汰策略 LRU 自定义淘汰策略
性能 单线程IO多线程(6.0+),QPS≈10万 多线程,QPS≈10万 本地调用,QPS≈100万
适用场景 分布式系统、复杂数据结构、高可用需求 简单key-value、高并发读 本地缓存、低延迟

面试追问:为什么Redis能替代Memcached成为主流?

答:Redis的数据结构多样性(如Sorted Set做排行榜、Hash存对象)、持久化能力(避免重启丢数据)、分布式支持(Cluster模式横向扩容)是核心优势。

三、核心原理深度解析

1. Redis持久化:数据安全的基石

持久化解决缓存宕机后数据恢复问题,需理解两种模式的原理与权衡:

(1)RDB(快照持久化)

  • 原理:定时(如每5分钟)将内存数据快照写入二进制文件(dump.rdb);
  • 触发方式:手动SAVE/BGSAVE(后台fork子进程)、自动配置;
  • 优点:文件小(压缩)、恢复快(直接加载快照);
  • 缺点:可能丢数据(如宕机前未触发快照);
  • 工程实践:生产环境很少单独用RDB,通常配合AOF。

(2)AOF(追加日志持久化)

  • 原理:记录所有写操作命令(如SET key value),以文本形式追加到文件(appendonly.aof);
  • 同步策略
    • always:每条命令同步刷盘(数据安全,性能差);
    • everysec:每秒刷盘(默认,平衡安全与性能);
    • no:由操作系统决定(性能好,数据风险高);
  • 重写机制:AOF文件过大时,自动合并重复命令(如多次INCR合并为SET);
  • 优点:数据安全性高(最多丢1秒数据);
  • 缺点:文件大、恢复慢(需重放所有命令)。

(3)混合持久化(Redis 4.0+)

  • 原理:AOF文件前半部分是RDB快照,后半部分是增量命令;
  • 优势:兼顾RDB的快速恢复与AOF的数据安全。

面试追问:如果Redis宕机,如何选择持久化策略?

答:电商系统(如库存)选everysec+混合持久化;日志系统(允许丢数据)选RDB。

2. Redis内存管理:避免OOM的关键

Redis的内存限制(通过maxmemory配置)是分布式缓存的核心约束,需理解淘汰策略

(1)淘汰策略分类

Redis的淘汰策略基于key的过期状态访问频率

  • volatile-*:仅淘汰有过期时间的key;
  • allkeys-*:淘汰所有key
  • lru/lfu:基于最近最少使用/最不经常使用;
  • ttl:淘汰剩余过期时间最短的key;
  • random:随机淘汰。

(2)工程选型建议

  • 若缓存数据均有过期时间:选allkeys-lru(优先淘汰最久未用的过期key);
  • 若缓存数据部分有过期时间:选volatile-lru+allkeys-lru组合(需测试);
  • 若需保留热点数据:选allkeys-lfu(更精准识别高频key)。

(3)LRU算法的Redis实现

  • 传统LRU:用双向链表维护访问顺序,每次访问将节点移到链表头部,淘汰尾部节点;
  • Redis LRU近似实现(节省内存)——每个key额外存储一个24位时钟(记录最后访问时间),淘汰时随机采样5个key,淘汰时钟最旧的。

面试追问:为什么Redis不用严格LRU?

答:严格LRU需要维护完整链表,内存开销大;近似LRU牺牲少量准确性,换取内存效率,符合Redis的轻量化设计。

3. Redis分布式模型:从主从到Cluster

分布式缓存的核心是横向扩容高可用,Redis的三种分布式模式需深入理解:

(1)主从复制(Master-Slave)

  • 原理:Slave节点复制Master的日志(RDB+AOF),实现数据同步;
  • 作用:读写分离(Master写、Slave读)、数据备份;
  • 问题:Master宕机后需手动切换,无自动容错。

(2)哨兵(Sentinel)

  • 原理:多个Sentinel节点监控Master/Slave状态,当Master宕机时,自动选举新的Master并调整Slave指向;
  • 核心功能:监控(Monitoring)、通知(Notification)、自动故障转移(Automatic Failover);
  • 工程实践:至少部署3个Sentinel节点(避免脑裂)。

(3)Cluster(集群)

  • 原理:将数据分片(Sharding)到多个节点,每个节点负责一部分槽位(共16384个槽);
  • 分片方式:通过CRC16(key) % 16384计算key所属槽位;
  • 高可用:每个节点有主从,主节点宕机后从节点晋升为主节点;
  • 访问方式:客户端需支持Cluster协议(如JedisCluster),自动路由到对应槽位的节点。

面试追问:Redis Cluster扩容时需要注意什么?

答:1. 新增节点后,需用redis-cli --cluster reshard重新分配槽位;2. 避免大key迁移(会导致节点阻塞);3. 迁移过程中需监控命中率(避免缓存失效)。

四、分布式场景下的关键问题与解决

1. 缓存穿透(Cache Penetration)

  • 定义:查询不存在的key,导致请求直接打到数据库(如查询不存在的用户ID);
  • 危害:数据库压力骤增,甚至宕机;
  • 解决方案
    1. 布隆过滤器(Bloom Filter):在缓存前加布隆过滤器,判断key是否存在——不存在直接返回,避免查数据库;
      • 缺点:有误判率(可通过增加哈希函数数量降低),需定期重建;
    2. Null缓存:若查询数据库不存在,缓存一个null值(设置短过期时间,如1分钟);
    3. 参数校验:前端/接口层校验key的合法性(如用户ID必须为正整数)。

面试追问:布隆过滤器的误判率怎么计算?

答:误判率≈(1 - e^(-kn/m))^k,其中k是哈希函数数量,n是已插入元素数,m是布隆过滤器大小。

2. 缓存击穿(Cache Breakdown)

  • 定义热点key过期瞬间,大量请求同时查询该key,导致请求打到数据库;

  • 危害:数据库瞬时压力过大;

  • 解决方案

    1. 热点key永不过期:后台任务定期更新缓存(如秒杀商品的库存缓存);

    2. 分布式锁:用Redis的SETNX或Redisson实现分布式锁,保证只有一个请求查数据库,其他请求等待缓存更新;

      • 示例代码(Redisson)

        RLock lock = redisson.getLock("product:stock:lock");
        try {
            if (lock.tryLock(0, 10, TimeUnit.SECONDS)) { // 尝试加锁,超时10秒
                // 查数据库更新缓存
                Product product = productDao.getById(id);
                redisTemplate.opsForValue().set("product:" + id, product, 1, TimeUnit.HOURS);
            }
        } finally {
            lock.unlock();
        }
        
    3. 提前更新:通过消息队列(如Kafka)发送key过期通知,提前更新缓存。

面试追问:分布式锁的过期时间怎么设置?

答:需大于查询数据库+更新缓存的时间(如10秒),避免锁提前释放导致并发问题;同时用“看门狗”机制(Redisson默认)自动续期。

3. 缓存雪崩(Cache Avalanche)

  • 定义大量缓存同时过期,导致请求全部打到数据库;
  • 危害:数据库宕机,系统不可用;
  • 解决方案
    1. 随机过期时间:给缓存设置不同的过期时间(如基础时间+随机1-5分钟);
    2. 多级缓存:本地缓存(如Guava Cache)+ Redis缓存——Redis失效后,本地缓存仍有数据;
    3. 熔断降级:用Sentinel/Hystrix设置数据库的熔断规则(如QPS超过100则熔断,返回默认值)。

4. 缓存一致性(Cache Consistency)

  • 定义:数据库与缓存数据不一致(如更新数据库后,缓存未更新);
  • 常见场景:Cache-Aside模式下,更新数据库后,缓存未删除;
  • 解决方案
    1. Cache-Aside(旁路缓存):最常用,但需严格遵循规则:
      • 读:先读缓存→无则读数据库→更新缓存;
      • 写:先更新数据库→删除缓存(而非更新缓存,避免并发问题);
    2. 延迟双删:更新数据库后,先删除缓存,再延迟1-2秒再次删除(解决“写后读不一致”);
    3. 消息队列异步更新:更新数据库后,发送消息到Kafka,消费者更新缓存(保证最终一致性);
    4. 分布式事务:用Seata做分布式事务,同时更新数据库与缓存(强一致性,性能差)。

面试追问:为什么Cache-Aside模式要“先更库再删缓存”,而不是“先删缓存再更库”?

答:若先删缓存,再更库,此时另一个请求读缓存→未命中→读数据库(旧值)→更新缓存(旧值),导致缓存中还是旧值。而先更库再删缓存,即使有并发读,最终缓存会被删除,下次读取会拿到新值。

五、工程实践细节与最佳实践

1. 缓存预热(Cache Warm-Up)

  • 定义:系统启动时,将热点数据加载到缓存;
  • 场景:电商大促前(如双11前加载热门商品缓存);
  • 实现:用定时任务(如Quartz)或启动脚本加载数据。

2. 大key处理

  • 定义:单个key的value过大(如>10KB);
  • 危害:查询慢、迁移阻塞、内存分配不均;
  • 解决方案
    1. 拆分key:将大Hash拆分为多个小Hash(如user:info:123user:info:123:base+user:info:123:ext);
    2. 使用SCAN命令:避免用KEYS命令遍历大key(会导致阻塞);
    3. 限制value大小:通过程序校验,禁止存入超过100KB的value。

3. 监控与运维

  • 关键指标
    • 命中率(Hit Rate):hit / (hit + miss),低于80%需排查;
    • 内存使用率:超过90%需扩容;
    • QPS:超过峰值需限流;
  • 工具
    • Redis自带的INFO命令(查看状态);
    • Prometheus+Grafana(监控可视化);
    • RedisInsight(GUI工具,排查问题)。

4. 最佳实践

  • key命名规范:用业务前缀+唯一标识(如order:info:123),方便管理与排查;
  • 避免复杂操作:不用KEYSFLUSHALL等危险命令;
  • 设置合理过期时间:根据业务场景设置(如用户信息缓存1小时,商品详情缓存30分钟);
  • 使用连接池:避免频繁创建连接(如JedisPool)。

六、与其他组件的整合

1. 与数据库整合

  • 读流程:Cache-Aside→读缓存→无则读数据库→更新缓存;
  • 写流程:先更库→删缓存(或异步更新缓存);
  • 事务配合:若写数据库失败,需回滚缓存的删除操作(或用消息队列重试)。

2. 与消息队列整合

  • 缓存失效通知:缓存过期时,发送消息到Kafka,消费者更新缓存;
  • 异步更新缓存:更新数据库后,发送消息,消费者异步更新缓存(减少数据库压力)。

3. 与分布式锁整合

  • 解决缓存击穿:热点key过期时,用分布式锁保证只有一个请求查数据库;
  • 解决并发更新:多个请求同时更新缓存时,用锁保证原子性。

面试模拟:从问题到追问

面试官:如果让你设计一个电商系统的商品详情缓存,你会考虑哪些点?

候选人:首先,用Redis做分布式缓存,key用product:info:{id},value存商品对象(Hash结构);然后,设置过期时间(如30分钟),避免缓存永久有效;接着,处理缓存穿透——用布隆过滤器判断商品ID是否存在;处理缓存击穿——热点商品(如秒杀商品)永不过期,用Redisson分布式锁防止并发查数据库;处理缓存雪崩——给过期时间加随机1-5分钟;最后,监控命中率,低于80%时排查问题。

面试官追问:布隆过滤器的误判率怎么处理?

候选人:可以增加布隆过滤器的大小(m),或者定期重建布隆过滤器(如每小时重建一次)。

面试官追问:如果Redis Cluster扩容,如何保证缓存命中率?

候选人:扩容时用redis-cli --cluster reshard重新分配槽位,尽量减少数据迁移;迁移过程中,监控命中率,若下降明显,暂停迁移并优化。

总结:分布式缓存的核心能力要求

  • 原理层面:理解Redis的持久化、内存管理、分布式模型;
  • 实践层面:能解决穿透/击穿/雪崩、一致性等问题;
  • 设计层面:能结合业务场景设计缓存方案(如电商、社交);
  • 运维层面:能监控、调优、扩容缓存集群。

掌握这些知识点,不仅能应对面试,更能解决实际系统中的缓存问题,成为资深架构师的核心竞争力。

2、Redis知识梳理

详见《5-2-3-组件选型》中Redis相关内容。

posted @ 2025-11-11 14:26  哈罗·沃德  阅读(3)  评论(0)    收藏  举报