跳到正文
Joeplover
后端开发·2026-06-25·约 3 分钟阅读

Redis 缓存优化:从 400 到 3078 QPS

在不修改原 CRUD 接口的前提下,通过 Redis Cache-Aside 模式新增缓存接口,配合压测验证 QPS 从几百提升到 3078 的完整过程。

服务器机架

Redis 缓存优化:从 400 到 3078 QPS

对比实验

在做 Java_Online 的性能测试时,我做了一组对比实验来直观感受缓存的效果:

无缓存

4 个线程并发查询 /courses 接口
数据库每次从 MySQL 读取,没有任何缓存层

结果:500 QPS,P95 响应时间 400ms

加 Redis 缓存

4 个线程并发查询 /courses 接口
数据在 Redis 中缓存(TTL 30 分钟)

结果:3078 QPS,P95 响应时间 12ms

6 倍提升——不是通过代码优化,只是加了一层缓存。

为什么缓存有这么大的提升

// 无缓存:每次都要查 MySQL
@GetMapping("/courses")
public List<Course> getCourses() {
    return courseRepository.findAll();  // 约 200ms SQL 查询
}

// 加缓存:90% 请求命中 Redis
@GetMapping("/courses")
public List<Course> getCourses() {
    List<Course> cached = redisTemplate.opsForValue().get("courses:all");
    if (cached != null) return cached;  // 不到 5ms
    
    List<Course> courses = courseRepository.findAll();
    redisTemplate.opsForValue().set("courses:all", courses, 30, TimeUnit.MINUTES);
    return courses;
}

差距主要来自:

  1. MySQL 查询:每次都要解析 SQL、执行查询、数据序列化——约 200ms
  2. Redis 查询:内存读取、网络传输——约 5ms
  3. 连接开销:MySQL 连接池有限,缓存命中后数据库连接释放给其他查询

而且 /courses 是读多写少的接口——课程列表一天才更新一两次,但可能被请求上万次。这样的接口缓存的收益最大。

Cache-Aside 模式

读:
  查 Redis → 命中 → 直接返回
          → 未命中 → 查 MySQL → 写入 Redis → 返回

写:
  更新 MySQL → 删除 Redis(下次读时重新加载)

这个模式稳定可靠,是生产环境最常用的缓存策略。

需要关注的坑

  1. 缓存与数据库一致性问题:更新数据库后删除缓存不是强一致方案。在极端并发下,读线程可能在缓存删除前读到旧数据。对于大部分应用这个短暂的不一致可以接受。

  2. 缓存雪崩:如果所有数据同时过期,所有请求会同时打到数据库。解决方案:TTL 加随机偏移。

  3. 缓存预热:服务刚启动时缓存是空的,第一批请求会穿透到数据库。可以在启动时做一次初始化加载:

@PostConstruct
public void warmup() {
    List<Course> courses = courseRepository.findAll();
    redisTemplate.opsForValue().set("courses:all", courses, 30, TimeUnit.MINUTES);
    log.info("缓存预热完成:加载了 {} 门课程", courses.size());
}

总结

一次简单的缓存集成带来了 6 倍的性能提升。这说明:

  1. 对于读多写少的接口,缓存是最立竿见影的优化手段
  2. 在优化代码之前,先考虑能否用缓存
  3. 需要关注一致性问题,确保缓存不会导致业务逻辑出错