【redis】使用redis作为缓存时所注意事项

缓存更新策略

在 Redis 缓存中,缓存的更新策略主要有**定期生成(定时更新)实时生成(即时更新)**两种方式。不同的策略适用于不同的业务场景,涉及性能、数据一致性和系统负载等方面的权衡。


1. 定期生成(定时更新)

是什么?

定期生成指的是按照固定的时间间隔,主动更新缓存,而不是在数据发生变化时立即更新。这种方式适用于数据变化不频繁、对实时性要求不高的场景。

优点:

降低数据库压力 :缓存可以批量更新,避免频繁查询数据库。

提高查询性能 :查询时直接读取缓存,响应速度快。

数据一致性较好(相对于长期不更新的缓存):定期更新可以保证数据不会长期过时。

缺点:

数据可能不够实时 :在缓存下一次更新前,数据可能已经变化,但缓存仍然返回旧数据。

不适合高实时性业务 :如果业务需要频繁变更数据,定期更新可能导致缓存数据滞后。

可能会引起短时流量冲击:如果所有缓存数据同时更新,可能会对数据库造成瞬间压力。

常见实现方式:

  • 定时任务更新缓存Time-based Refresh

    • 使用 Spring Task、Quartz、Crontab 等定时任务,每隔一段时间刷新缓存。

    • 例如,每 10 分钟更新一次缓存:

      java 复制代码
      @Scheduled(fixedRate = 600000) // 每 10 分钟执行一次
      public void updateCache() {
          // 查询数据库并更新缓存
          List<Data> dataList = databaseService.getData();
          redisTemplate.opsForValue().set("cache:data", dataList);
      }
  • 数据库变更时触发缓存更新Database-triggered Refresh

    • 监听 数据库变更事件(MySQL Binlog、PostgreSQL 触发器),检测到数据变化后批量刷新缓存。
  • 异步任务更新

    • 使用消息队列(Kafka、RabbitMQ)通知服务更新缓存,避免定时任务导致的瞬时数据库压力过大。
适用场景:

📌 统计数据、排行榜、热门商品列表等(更新频率较低,数据稍有延迟也无大问题)。

📌 日志分析、报表数据等(数据量大,但对实时性要求不高)。


2. 实时生成(即时更新)

是什么?

实时生成指的是数据发生变更时立即更新缓存 ,确保缓存数据始终是最新的。这种方式适用于对数据一致性要求高、变更较频繁的场景。

优点:

数据实时性高 :缓存的数据始终与数据库保持一致,适用于高实时性需求的应用。

避免缓存不一致问题:数据库变更后立即同步缓存,减少数据不匹配的情况。

缺点:

更新成本高 :每次数据变更都需要更新缓存,可能会导致数据库压力增大。

可能导致缓存频繁更新 :对于高频变更的数据,频繁更新可能会导致 Redis 负载过重,甚至影响整体性能。

并发问题 :多个并发请求可能会导致缓存不一致缓存击穿,需要加锁或使用双写策略。

常见实现方式:

  1. 数据库更新时主动更新缓存Write-through Strategy

    • 在 **数据更新(新增、修改、删除)**时,同时更新数据库和缓存

      java 复制代码
      public void updateData(Data data) {
          databaseService.updateData(data); // 更新数据库
          redisTemplate.opsForValue().set("cache:data:" + data.getId(), data); // 同步更新缓存
      }
    • 适用于数据变更不频繁,且一致性要求较高的场景。

  2. 缓存淘汰(Cache Eviction)

    • 在数据库更新后,删除缓存 ,让下一次查询时重新加载数据:

      java 复制代码
      public void updateData(Data data) {
          databaseService.updateData(data); // 更新数据库
          redisTemplate.delete("cache:data:" + data.getId()); // 删除缓存
      }
    • 适用于缓存数据不是热点,数据变更后不需要立即被查询的情况。

  3. 订阅数据库变更Event-based Strategy

    • 使用 消息队列(Kafka、RabbitMQ)Redis 订阅/发布机制,监听数据库变更事件,变更后更新缓存。
  4. 分布式锁(避免缓存并发写入问题)

    • 解决多个请求同时更新缓存导致数据不一致的问题:

      java 复制代码
      RLock lock = redissonClient.getLock("cache:lock:data:" + data.getId());
      try {
          if (lock.tryLock(5, TimeUnit.SECONDS)) {
              databaseService.updateData(data);
              redisTemplate.opsForValue().set("cache:data:" + data.getId(), data);
          }
      } finally {
          lock.unlock();
      }
    • 适用于高并发写入场景,防止缓存同时被多个请求覆盖。

适用场景:

📌 订单系统、支付系统、库存管理等(数据必须实时更新,不能有延迟)。

📌 直播、弹幕系统(数据实时变化,需要确保一致性)。


总结:定期生成 vs. 实时生成

策略 定期生成(定时更新) 实时生成(即时更新)
数据实时性 低(有一定延迟) 高(数据库更新即缓存更新)
数据库压力 低(定期批量更新) 高(频繁更新缓存)
缓存命中率 高(查询时直接命中缓存) 可能较低(某些情况需删除缓存)
适用场景 排行榜、统计数据、报表等 订单、库存、支付等高一致性业务

总结

  • 定期生成(定时更新) 适用于数据变化不频繁对实时性要求不高的场景,如排行榜、日志分析等。
  • 实时生成(即时更新) 适用于数据变化频繁对一致性要求高的场景,如支付、库存、订单管理等。
  • 在实际应用中,可以结合两种策略 ,例如:
    • 定期更新 + 变更触发更新:大部分数据定期刷新,关键数据实时更新。
    • 读时更新 + 写时淘汰:查询时自动更新缓存,写入时删除缓存,防止数据不一致。

合理选择缓存更新策略,可以有效提升系统性能,降低数据库压力,并保证数据的一致性。

Redis 作为缓存,存储空间有限,因此需要淘汰数据来保证新数据的存入。Redis 提供了多种缓存淘汰策略(Eviction Policy),用于决定哪些数据需要被删除。下面介绍几种常见的淘汰策略,包括它们的适用场景和优缺点。


缓存淘汰策略

1. 不淘汰策略

1.1 noeviction(拒绝写入)

概念:

当 Redis 内存占满时,不会删除任何已有数据,而是直接返回错误,拒绝新的写入请求。

适用场景:
  • 适用于严格不能丢数据的场景,如任务队列(消息队列)、金融交易等。
  • 适用于 Redis 作为纯数据存储而非缓存时。
优缺点:

数据不会被误删除 ,保证数据完整性。

可能导致写入失败,影响系统稳定性。


2. 基于 TTL(过期时间)的淘汰策略

2.1 volatile-lru(最近最少使用,TTL 限定)

概念:
  • 只淘汰**设置了过期时间(TTL)**的键。
  • 在这些键中,优先删除最近最少使用(LRU, Least Recently Used)的数据。
适用场景:
  • 适用于部分数据可丢弃的场景,比如 session、短期缓存数据。
  • 适用于需要自动过期控制,但仍希望尽可能保留热点数据的情况。
优缺点:

优先保留常用数据,减少缓存击穿的概率。

如果大部分 key 没有 TTL,可能导致 Redis 直接拒绝写入(相当于 noeviction)。


2.2 volatile-ttl(优先淘汰即将过期的键)

概念:
  • 只淘汰**设置了过期时间(TTL)**的键。
  • 其中剩余寿命最短的键优先被删除。
适用场景:
  • 适用于对数据有明确的生命周期需求的业务,如订单缓存、验证码缓存等。
优缺点:

优先删除即将过期的数据,保证短期缓存的更新。

可能误删仍然有价值的热点数据。


3. 基于数据访问频率的淘汰策略

3.1 allkeys-lru(全局最近最少使用)

概念:
  • 无视 TTL ,从所有键中(包括没有设置 TTL 的键),优先淘汰最近最少使用的键。
适用场景:
  • 适用于热点数据更新频繁的场景,如推荐系统、排行榜、搜索结果缓存等。
优缺点:

可以确保常用数据长期保留,提高缓存命中率。

如果热点数据突然减少访问,可能会被错误淘汰。


3.2 allkeys-random(全局随机淘汰)

概念:
  • 无视 TTL ,在所有 key 中随机删除某些数据。
适用场景:
  • 适用于缓存数据均匀访问,不需要特定优先级的场景。
优缺点:

简单高效,减少淘汰策略的计算开销。

不够智能,可能淘汰热点数据,降低缓存命中率。


4. 基于数据访问频次的淘汰策略

4.1 volatile-lfu(基于访问频率,TTL 限定)

概念:
  • 只淘汰设置了 TTL 的 key
  • 访问次数最少的键优先被删除(LFU, Least Frequently Used)。
适用场景:
  • 适用于需要根据访问次数保留数据的业务,如热点文章缓存、用户历史记录等。
优缺点:

能够长期保留高频访问数据,淘汰低频数据。

如果大部分数据没有 TTL,可能导致 Redis 拒绝写入(类似 noeviction)。


4.2 allkeys-lfu(全局最不常使用淘汰)

概念:
  • 无视 TTL ,从所有键中优先淘汰访问次数最少的键
适用场景:
  • 适用于热点数据访问有明显差异的情况,如新闻热点推荐、热门产品缓存等。
优缺点:

能保留长期热点数据,提高缓存命中率。

短期热点数据可能无法及时替换,导致数据更新滞后。


总结

策略 机制 适用场景 优点 缺点
noeviction 拒绝写入 不能丢数据(消息队列、金融) 数据安全 容易写满导致错误
volatile-lru 仅淘汰 TTL 数据,LRU 需自动过期,保留热点数据 减少缓存击穿 仅适用于部分数据有 TTL
volatile-ttl 仅淘汰 TTL 数据,剩余寿命短的优先 订单缓存、验证码 优先清理即将失效的缓存 可能误删热点数据
allkeys-lru 全局 LRU 淘汰 访问频率高的缓存(推荐系统) 提高缓存命中率 可能误删突然冷却的热点数据
allkeys-random 随机淘汰 数据访问均匀的缓存 计算开销小 可能淘汰重要数据
volatile-lfu 仅淘汰 TTL 数据,访问最少的优先 需要根据访问频率保留数据 长期热点数据保留 仅适用于有 TTL 的 key
allkeys-lfu 全局 LFU 淘汰 热点明显的数据(新闻、直播) 缓存命中率高 短期热点更新慢

如何选择淘汰策略?

1. 数据不能丢失(消息队列、金融)

noeviction(拒绝写入)

2. 仅淘汰过期数据(业务数据自动失效)

volatile-lru (保留热点)

volatile-ttl(优先清理快过期数据)

3. 需要智能保留高频访问数据

allkeys-lru (最近最少使用淘汰)

allkeys-lfu(最少使用淘汰)

4. 访问数据均匀,不关心淘汰顺序

allkeys-random(随机删除)

5. 业务需要权衡 LRU 和 LFU

  • 短期热点多 ,选 LRU
  • 长期热点多 ,选 LFU

结论

  • 如果数据有 TTL,且希望优先淘汰冷数据 ,选 volatile-lru / volatile-lfu
  • 如果所有数据都可以被淘汰 ,选 allkeys-lru / allkeys-lfu
  • 如果只允许写满后拒绝写入 ,选 noeviction
  • 如果对淘汰规则无特别要求 ,选 allkeys-random

正确选择淘汰策略,可以有效提高缓存命中率,降低数据库压力,保障系统稳定性。

常见缓存问题

在 Redis 中,缓存预热、缓存穿透、缓存雪崩和缓存击穿是常见的缓存问题。下面分别描述它们的概念及解决方案:


1. 缓存预热(Cache Warming)

是什么?

缓存预热是指在系统启动或运行之前,提前将热点数据加载到缓存中,以减少数据库的查询压力,提高系统访问速度。

如何解决?
  • 手动加载:在服务启动时,手动将热点数据写入缓存。
  • 定时刷新:通过定时任务(如 Spring Task、Quartz 等)定期加载热点数据到缓存。
  • 数据变更同步:监听数据库更新(如 MySQL binlog、Redis 订阅发布机制),在数据变化时同步更新缓存。
  • 批量加载 :使用 Redis 的 pipelinemset 命令批量写入缓存,提高加载效率。

2. 缓存穿透(Cache Penetration)

是什么?

缓存穿透指的是大量请求查询不存在的数据,导致每次请求都要查询数据库,缓存完全失效,给数据库带来巨大压力。

如何解决?
  • 缓存空值 :如果查询的数据不存在,可以将空值(如 null{})存入缓存,并设置较短的过期时间,避免重复查询数据库。
  • 布隆过滤器(Bloom Filter):使用布隆过滤器提前判断某个 key 是否可能存在,如果一定不存在,则直接返回,不查询数据库。
  • 参数校验:在请求层对参数进行校验,避免无效请求进入系统。
  • 限流与黑名单:对异常请求 IP 进行封禁或限流,避免恶意攻击。

3. 缓存雪崩(Cache Avalanche)

是什么?

缓存雪崩指的是大量缓存同时失效,导致短时间内大量请求直接打到数据库,造成数据库压力激增,甚至宕机。

如何解决?
  • 缓存过期时间随机化 :为缓存设置不同的过期时间 ,避免大量缓存同时失效,例如使用 TTL = 基础时间 ± 随机时间
  • 热点数据提前预加载:在缓存即将过期前,主动刷新缓存,保证热点数据始终可用。
  • 双层缓存 :使用 Redis + 本地缓存(如 Caffeine、Guava Cache),降低对 Redis 的依赖。
  • 流量削峰
    • 限流:使用限流算法(如令牌桶、漏桶)限制访问速率。
    • 降级:当数据库压力过大时,返回默认值或降级处理。

4. 缓存击穿(Cache Breakdown)

是什么?

缓存击穿指的是某个热点 key 突然失效,导致大量并发请求直接打到数据库,造成数据库短时间内压力剧增。

如何解决?
  • 设置热点数据永不过期:对于热点数据,直接不设置过期时间,而是由业务逻辑主动更新缓存。
  • 互斥锁(分布式锁)
    • 当缓存失效时,多个请求只允许一个线程查询数据库并更新缓存,其他线程等待缓存更新完成后再读取。
    • 具体实现:使用 SETNX(Redis 分布式锁) 或 Redisson。
  • 提前刷新缓存
    • 通过异步线程提前更新即将过期的热点缓存,防止突然失效。
    • 例如:使用 Redis + 过期监听,在 key 即将过期前主动更新缓存。

总结

问题 现象 解决方案
缓存预热 缓存刚启动时,没有数据 手动加载、定时刷新、监听数据变更
缓存穿透 查询的 key 在数据库中不存在,每次都查数据库 缓存空值、布隆过滤器、参数校验、黑名单
缓存雪崩 大量 key 同时失效,数据库压力激增 过期时间随机化、双层缓存、限流、降级
缓存击穿 某个热点 key 失效,大量请求打到数据库 热点数据永不过期、分布式锁、提前刷新

这四个缓存问题都是分布式系统中必须重点关注的,合理的缓存策略可以有效提升系统的性能和稳定性。

相关推荐
呼拉拉呼拉18 分钟前
Redis故障转移
数据库·redis·缓存·高可用架构
什么都想学的阿超21 分钟前
【Redis系列 04】Redis高可用架构实战:主从复制与哨兵模式从零到生产
数据库·redis·架构
我命由我123453 小时前
Spring Boot 项目集成 Redis 问题:RedisTemplate 多余空格问题
java·开发语言·spring boot·redis·后端·java-ee·intellij-idea
篱笆院的狗4 小时前
如何使用 Redis 快速实现布隆过滤器?
数据库·redis·缓存
小鸡脚来咯5 小时前
redis分片集群架构
数据库·redis·架构
什么都想学的阿超5 小时前
【Redis系列 03】掌握Redis编程艺术:事务、管道与Lua脚本完全指南
redis·junit·lua
Alla T5 小时前
【前端】缓存相关
前端·缓存
想用offer打牌9 小时前
面试官问:Redis和MySQL数据一致,为什么还需要MySQL?🤠
数据库·redis·mysql
chen.@-@9 小时前
后端下载限速(redis记录实时并发,bucket4j动态限速)
数据库·redis·缓存
呼拉拉呼拉9 小时前
Redis高可用架构
数据库·redis·架构·高可用架构