
1) 【一句话结论】
我们通过引入Redis缓存、数据库分库分表及异步任务处理,将课程搜索响应时间从1.5秒优化至0.2秒,QPS从约500提升至3000+,性能提升约86%。
2) 【原理/概念讲解】
老师口吻:同学们,咱们先看问题背景——教育系统课程搜索在高并发场景下(如开学季、考试季),用户搜索课程时响应慢,核心原因是数据库压力过大(大量请求同时查询课程表,导致数据库CPU/IO饱和)。优化方案的核心是“分层解耦+负载分散”:
3) 【对比与适用场景】
| 方案 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| Redis缓存 | 内存数据库,支持数据结构(列表、集合、哈希等) | 高速读写,内存管理灵活,支持持久化 | 热点数据查询(如课程搜索结果、用户行为数据) | 需防缓存击穿/雪崩,需设置过期时间,数据结构选择影响性能 |
| Memcached | 简单键值对缓存 | 速度快,适合简单数据存储 | 热点数据查询(如简单键值对,如用户ID-用户名) | 数据结构单一,不支持持久化,适合轻量级场景 |
| 数据库分库分表 | 将大表拆分成多小表,分散到多库多表 | 减少单库压力,提升并发 | 数据量大的核心表(如课程表、用户表) | 需处理数据一致性(如更新缓存与数据库同步),分片规则设计需合理(如哈希分片避免热点表) |
| 异步任务(RabbitMQ) | 消息队列,解耦业务 | 解耦生产者与消费者,支持持久化、事务 | 非实时业务(如搜索结果更新、日志处理) | 需考虑消息丢失、延迟,需事务机制保证可靠性 |
4) 【示例】
伪代码示例(搜索请求处理流程,含缓存穿透防护、击穿防护、雪崩防护):
public String searchCourse(String keyword) {
// 1. 布隆过滤器判断缓存穿透
if (!isInBloomFilter(keyword)) {
return "未找到";
}
// 2. 查Redis缓存(互斥锁防击穿)
String result = redisTemplate.opsForValue().get("course:" + keyword);
if (result != null) {
return result;
}
// 尝试加互斥锁(Redis SETNX)
if (redisTemplate.opsForValue().setIfAbsent("lock:" + keyword, "1", 10, TimeUnit.SECONDS)) {
try {
// 3. 查数据库(分库分表查询,按学科ID哈希分片)
Course course = courseMapper.selectByKeywordWithSharding(keyword);
if (course != null) {
// 4. 更新Redis缓存(随机过期时间防雪崩)
redisTemplate.opsForValue().set("course:" + keyword, course.toJson(),
RandomUtil.randomInt(30, 60), TimeUnit.SECONDS);
return course.toJson();
}
} finally {
// 释放锁
redisTemplate.delete("lock:" + keyword);
}
} else {
// 5. 短暂重试
Thread.sleep(100);
return searchCourse(keyword);
}
return "未找到";
}
// 布隆过滤器初始化(假设已初始化)
private boolean isInBloomFilter(String keyword) {
return bloomFilter.contains(keyword);
}
5) 【面试口播版答案】
我之前参与过超星教育系统的课程搜索优化项目,当时用户搜索课程时响应慢,尤其在开学季,平均响应时间约1.5秒,QPS只有500左右。我们优化方案是分三步:第一步引入Redis作为缓存层,缓存热门课程的搜索结果;第二步对课程表进行分库分表,按学科ID进行哈希分片,把数据分散到多个数据库实例,减少单库压力;第三步将非实时的搜索结果更新改为异步任务,用RabbitMQ处理,避免阻塞搜索请求。技术选型上,缓存用了Redis(因为支持JSON存储课程对象,数据结构丰富),分库分表用了MyBatis-Plus的分库插件,异步处理用了RabbitMQ。优化后,响应时间从1.5秒降到0.2秒,QPS提升到3000+,性能提升约86%,用户搜索体验显著改善。
6) 【追问清单】
7) 【常见坑/雷区】