Spring Data Redis 实战避坑指南:从配置到缓存预热的全链路最佳实践

文章目录

    • 一、缓存的正确打开方式(先立规矩)
      • [1️⃣ 明确 Redis 在你项目里的角色](#1️⃣ 明确 Redis 在你项目里的角色)
    • [二、Spring Data Redis的配置](#二、Spring Data Redis的配置)
      • [1. 引入依赖](#1. 引入依赖)
      • [2. 配置文件](#2. 配置文件)
    • [三、序列化:80% 的坑从这里开始 💣](#三、序列化:80% 的坑从这里开始 💣)
      • [❌ 默认 JDK 序列化(强烈不推荐)](#❌ 默认 JDK 序列化(强烈不推荐))
      • [✅ 推荐:JSON 序列化(Jackson)](#✅ 推荐:JSON 序列化(Jackson))
    • [四、缓存 Key 设计规范(非常重要)](#四、缓存 Key 设计规范(非常重要))
      • [❌ 反例](#❌ 反例)
      • [✅ 推荐规范](#✅ 推荐规范)
    • [五、TTL:缓存没有过期时间就是定时炸弹 ⏰](#五、TTL:缓存没有过期时间就是定时炸弹 ⏰)
      • [❌ 错误示例](#❌ 错误示例)
      • [✅ 正确姿势](#✅ 正确姿势)
      • [💡 建议 TTL 规则](#💡 建议 TTL 规则)
    • 六、缓存读写模式(最核心)
      • [✅ 1️⃣ Cache-Aside(旁路缓存)【首选】](#✅ 1️⃣ Cache-Aside(旁路缓存)【首选】)
    • [七、避免缓存常见"三大杀手" 🔪](#七、避免缓存常见“三大杀手” 🔪)
      • [1️⃣ 缓存穿透](#1️⃣ 缓存穿透)
      • [2️⃣ 缓存击穿(热点 key 失效)](#2️⃣ 缓存击穿(热点 key 失效))
      • [3️⃣ 缓存雪崩](#3️⃣ 缓存雪崩)
    • 八、缓存预热
      • [1. 为什么要进行缓存预热?](#1. 为什么要进行缓存预热?)
      • [2. 缓存预热的几种常见方案](#2. 缓存预热的几种常见方案)
        • [✅ 方案 1:系统启动时预热(最常用)](#✅ 方案 1:系统启动时预热(最常用))
        • 常用方式
        • [✅ 方案 2:定时任务预热(生产级)](#✅ 方案 2:定时任务预热(生产级))
        • [✅ 方案 3:运维脚本 / 管理后台触发(最稳)](#✅ 方案 3:运维脚本 / 管理后台触发(最稳))

一、缓存的正确打开方式(先立规矩)

1️⃣ 明确 Redis 在你项目里的角色

Redis 不只是缓存,它常见有 4 种身份:

角色 是否推荐用 Spring Data Redis
数据缓存 ✅ 强烈推荐
分布式锁 ⚠️ 可用,但更推荐 Redisson
消息队列 ❌ 不推荐
会话 / 登录态 ✅ 推荐

👉 Spring Data Redis 最擅长:缓存 & KV 访问


二、Spring Data Redis的配置

1. 引入依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. 配置文件

yaml 复制代码
# Redis 配置
spring:
  redis:
    database: 0
    host: xxxx
    port: xxx
    timeout: 2000
    password: xxx

三、序列化:80% 的坑从这里开始 💣

❌ 默认 JDK 序列化(强烈不推荐)

  • 数据不可读

  • 占空间大

  • 跨语言不友好

  • 改类结构就反序列化失败

✅ 推荐:JSON 序列化(Jackson)

java 复制代码
@Configuration
public class RedisConfiguration {

    @Bean
    public RedisTemplate<String, Object> stringObjectRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(RedisSerializer.string());
        template.setValueSerializer(RedisSerializer.json());
        return template;
    }
}

四、缓存 Key 设计规范(非常重要)

❌ 反例

java 复制代码
user
data

✅ 推荐规范

java 复制代码
业务:模块:标识

示例:

less 复制代码
user:info:1001 
order:detail:20250101_88 
product:stock:sku123

📌 好处

  • 一眼能看懂

  • 支持按前缀批量清理

  • 防止 key 冲突


五、TTL:缓存没有过期时间就是定时炸弹 ⏰

❌ 错误示例

java 复制代码
redisTemplate.opsForValue().set(key, value);

✅ 正确姿势

java 复制代码
redisTemplate.opsForValue().set( key, value, 10, TimeUnit.MINUTES );

💡 建议 TTL 规则

数据类型 TTL
用户信息 5~30 分钟
配置信息 1~24 小时
热点数据 1~5 分钟
登录态 与 token 有效期一致

六、缓存读写模式(最核心)

✅ 1️⃣ Cache-Aside(旁路缓存)【首选】

读流程

text 复制代码
查 Redis → 没有 → 查 DB → 写 Redis → 返回

写流程

text 复制代码
更新 DB → 删除 Redis

📌 示例:

java 复制代码
public User getUser(Long id) {
    String key = "user:info:" + id;
    User user = (User) redisTemplate.opsForValue().get(key);
    if (user != null) {
        return user;
    }

    user = userMapper.selectById(id);
    if (user != null) {
        redisTemplate.opsForValue().set(key, user, 10, TimeUnit.MINUTES);
    }
    return user;
}

七、避免缓存常见"三大杀手" 🔪

1️⃣ 缓存穿透

问题:请求不存在的数据

解决方案

  • 空对象缓存(短 TTL)

  • 布隆过滤器

java 复制代码
redisTemplate.opsForValue().set(key, "NULL", 1, TimeUnit.MINUTES);

2️⃣ 缓存击穿(热点 key 失效)

问题:热点 key 同时失效,大量请求打 DB

解决方案

  • 互斥锁(synchronized / Redis 锁)

  • 逻辑过期

java 复制代码
synchronized (this) { // double check }

3️⃣ 缓存雪崩

问题:大量 key 同时过期

解决方案

  • TTL 加随机值

  • 分批过期

java 复制代码
int ttl = 600 + new Random().nextInt(60);

八、缓存预热

1. 为什么要进行缓存预热?

如果不预热,系统刚启动时会出现这些名场面:

  • 🚨 冷启动雪崩:大量请求同时穿透到数据库

  • 🐌 首批用户体验极差:第一次访问慢如老牛

  • 💥 DB 瞬时压力暴增:高并发下容易崩

一句话总结:

👉 缓存不热,系统就会感冒。

2. 缓存预热的几种常见方案

✅ 方案 1:系统启动时预热(最常用)

适合:热点数据明确、数据量可控

在 Spring Boot 启动完成后,把数据提前塞进 Redis。

常用方式
  • @PostConstruct

  • ApplicationRunner

  • CommandLineRunner(推荐)

java 复制代码
@Component
public class CachePreheatRunner implements ApplicationRunner {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ProductService productService;

    @Override
    public void run(ApplicationArguments args) {
        List<Product> hotList = productService.queryHotProducts();
        for (Product p : hotList) {
            redisTemplate.opsForValue()
                .set("product:" + p.getId(), p, 30, TimeUnit.MINUTES);
        }
        System.out.println("🔥 缓存预热完成");
    }
}

✨ 特点:

  • 启动即完成预热

  • 简单直接

  • 最常见于中小系统


✅ 方案 2:定时任务预热(生产级)

适合:热点会变化、数据需要动态更新

java 复制代码
@Scheduled(cron = "0 0/10 * * * ?")
public void refreshHotCache() {
    List<Product> hotList = productService.queryHotProducts();
    hotList.forEach(p -> {
        redisTemplate.opsForValue()
            .set("product:" + p.getId(), p, 30, TimeUnit.MINUTES);
    });
}

✨ 特点:

  • 热点随业务变化

  • 避免缓存过期集中失效

  • 电商、活动系统常用


✅ 方案 3:运维脚本 / 管理后台触发(最稳)

适合:发布后、流量切换前

  • 管理后台点按钮

  • 运维脚本调用接口

  • 灰度发布前先预热

http 复制代码
POST /cache/preheat

✨ 特点:

  • 可控、可回滚

  • 适合大系统、集群部署

相关推荐
七夜zippoe10 小时前
数据库事务隔离级别与Spring传播行为深度解析
java·数据库·spring·mvcc·acid·myslq
Stecurry_3010 小时前
Springmvc理解从0到1 完整代码详解
java·spring boot·spring
win x10 小时前
Redis 哨兵模式
数据库·redis·缓存
钦拆大仁10 小时前
如何手搓一个Spring Security
java·后端·spring
廋到被风吹走12 小时前
【Spring】AOP深度解析:代理机制、拦截器链与事务失效全解
java·spring·缓存
ohoy12 小时前
RedisTemplate 使用之Hash
redis·算法·哈希算法
步步为营DotNet12 小时前
深度探索.NET 中ValueTask:优化异步性能的轻量级利器
java·spring·.net
heartbeat..12 小时前
Spring 声明式事务:原理、使用及失效场景详解
java·spring·面试·事务
何中应12 小时前
关于查询方式的总结与讨论
后端·缓存·查询