Redis 缓存优化方案
📅 创建日期:2025-10-27
📌 版本:v1.0
一、系统缓存现状分析
1.1 当前 Redis 配置
配置文件:config/application.yml
spring:
redis:
host: localhost
port: 6379
database: 0
password:
timeout: 10s
lettuce:
pool:
min-idle: 0
max-idle: 8
max-active: 8
max-wait: -1ms
现状:
- ✅ 已配置 Redis 连接
- ✅ 已有
RedisCache工具类 - ⚠️ 连接池较小(max-active: 8)
- ⚠️ 业务层缓存使用不充分
- ⚠️ 缺少统一的缓存策略
1.2 系统业务特点分析
基于代码分析,系统有以下业务特点:
| 业务模块 | 读写比例 | 数据变化频率 | 缓存需求优先级 |
|---|---|---|---|
| 字典数据 | 读多写少 (90:10) | 低 | ⭐⭐⭐⭐⭐ |
| 客户信息 | 读多写少 (80:20) | 低 | ⭐⭐⭐⭐⭐ |
| 设备信息 | 读多写少 (85:15) | 中 | ⭐⭐⭐⭐ |
| 检查模板 | 读多写少 (95:5) | 低 | ⭐⭐⭐⭐⭐ |
| 技术员信息 | 读多写少 (75:25) | 中 | ⭐⭐⭐⭐ |
| 工单列表 | 读多写少 (70:30) | 高 | ⭐⭐⭐ |
| 统计数据 | 读多写少 (95:5) | 高 | ⭐⭐⭐⭐⭐ |
| 用户权限 | 读多写少 (99:1) | 低 | ⭐⭐⭐⭐⭐ |
二、缓存策略设计
2.1 缓存层级设计
┌─────────────────────────────────────────────────┐
│ 应用层 │
├─────────────────────────────────────────────────┤
│ L1: 本地缓存 (Caffeine - 可选) │
│ - 字典数据 (5分钟) │
│ - 用户权限 (10分钟) │
├─────────────────────────────────────────────────┤
│ L2: Redis 分布式缓存 │
│ - 业务对象缓存 │
│ - 列表缓存 │
│ - 统计数据缓存 │
├─────────────────────────────────────────────────┤
│ 数据库层 │
└─────────────────────────────────────────────────┘2.2 缓存键命名规范
统一使用冒号分隔的命名方式:
{项目名}:{模块}:{业务}:{标识}
示例:
husky:customer:detail:1001 # 客户详情
husky:equipment:list:project:2001 # 项目设备列表
husky:workorder:stats:daily # 工单每日统计
husky:dict:type:maintenance_type # 字典数据
husky:user:permissions:admin # 用户权限2.3 缓存过期策略
| 数据类型 | 过期时间 | 更新策略 | 说明 |
|---|---|---|---|
| 基础数据 | 24小时 | 主动刷新 | 客户、项目、设备类型等 |
| 业务对象 | 1小时 | 被动失效 | 工单详情、设备详情等 |
| 列表数据 | 10分钟 | 主动失效 | 工单列表、设备列表等 |
| 统计数据 | 30分钟 | 定时刷新 | Dashboard 统计数据 |
| 字典数据 | 永久 | 主动刷新 | 系统字典、配置数据 |
| 用户会话 | 30分钟 | 自动续期 | Token、权限信息 |
三、具体实现方案
3.1 优化 Redis 配置
文件:config/application.yml
spring:
redis:
host: localhost
port: 6379
database: 0
password:
timeout: 10s
lettuce:
pool:
# 最小空闲连接数
min-idle: 5
# 最大空闲连接数
max-idle: 20
# 最大活跃连接数(根据并发量调整)
max-active: 50
# 连接池最大阻塞等待时间
max-wait: 3s
# 关闭超时时间
shutdown-timeout: 100ms
调整说明:
max-active: 50- 支持更高并发max-idle: 20- 保持足够的空闲连接min-idle: 5- 避免频繁创建连接
3.2 创建缓存常量类
文件:src/main/java/com/husky/common/constant/CacheConstants.java
package com.husky.common.constant;
/**
* 缓存常量
*
* @author husky
*/
public class CacheConstants
{
/**
* 缓存键前缀
*/
public static final String CACHE_PREFIX = "husky:";
/**
* 客户模块缓存键
*/
public static final String CUSTOMER_KEY = CACHE_PREFIX + "customer:";
public static final String CUSTOMER_DETAIL_KEY = CUSTOMER_KEY + "detail:";
public static final String CUSTOMER_LIST_KEY = CUSTOMER_KEY + "list";
/**
* 项目模块缓存键
*/
public static final String PROJECT_KEY = CACHE_PREFIX + "project:";
public static final String PROJECT_DETAIL_KEY = PROJECT_KEY + "detail:";
public static final String PROJECT_LIST_KEY = PROJECT_KEY + "list";
/**
* 设备模块缓存键
*/
public static final String EQUIPMENT_KEY = CACHE_PREFIX + "equipment:";
public static final String EQUIPMENT_DETAIL_KEY = EQUIPMENT_KEY + "detail:";
public static final String EQUIPMENT_LIST_KEY = EQUIPMENT_KEY + "list:project:";
public static final String EQUIPMENT_TYPE_KEY = EQUIPMENT_KEY + "type:";
/**
* 工单模块缓存键
*/
public static final String WORKORDER_KEY = CACHE_PREFIX + "workorder:";
public static final String WORKORDER_DETAIL_KEY = WORKORDER_KEY + "detail:";
public static final String WORKORDER_LIST_KEY = WORKORDER_KEY + "list";
public static final String WORKORDER_STATS_KEY = WORKORDER_KEY + "stats:";
/**
* 子任务模块缓存键
*/
public static final String SUBTASK_KEY = CACHE_PREFIX + "subtask:";
public static final String SUBTASK_DETAIL_KEY = SUBTASK_KEY + "detail:";
public static final String SUBTASK_LIST_KEY = SUBTASK_KEY + "list:order:";
/**
* 检查模板缓存键
*/
public static final String TEMPLATE_KEY = CACHE_PREFIX + "template:";
public static final String TEMPLATE_DETAIL_KEY = TEMPLATE_KEY + "detail:";
public static final String TEMPLATE_LIST_KEY = TEMPLATE_KEY + "list";
/**
* 技术员缓存键
*/
public static final String TECHNICIAN_KEY = CACHE_PREFIX + "technician:";
public static final String TECHNICIAN_DETAIL_KEY = TECHNICIAN_KEY + "detail:";
public static final String TECHNICIAN_LIST_KEY = TECHNICIAN_KEY + "list";
public static final String TECHNICIAN_AVAILABLE_KEY = TECHNICIAN_KEY + "available";
/**
* 备件缓存键
*/
public static final String SPARE_PART_KEY = CACHE_PREFIX + "sparepart:";
public static final String SPARE_PART_DETAIL_KEY = SPARE_PART_KEY + "detail:";
public static final String SPARE_PART_INVENTORY_KEY = SPARE_PART_KEY + "inventory:";
/**
* 统计数据缓存键
*/
public static final String STATS_KEY = CACHE_PREFIX + "stats:";
public static final String STATS_DASHBOARD_KEY = STATS_KEY + "dashboard";
public static final String STATS_WORKORDER_KEY = STATS_KEY + "workorder:";
public static final String STATS_TECHNICIAN_KEY = STATS_KEY + "technician:";
/**
* 字典缓存键
*/
public static final String DICT_KEY = CACHE_PREFIX + "dict:";
/**
* 用户权限缓存键
*/
public static final String USER_PERMISSION_KEY = CACHE_PREFIX + "user:permissions:";
public static final String ROLE_CUSTOMER_KEY = CACHE_PREFIX + "role:customer:";
/**
* 缓存过期时间(秒)
*/
public static final long EXPIRE_24H = 24 * 60 * 60; // 24小时
public static final long EXPIRE_1H = 60 * 60; // 1小时
public static final long EXPIRE_30M = 30 * 60; // 30分钟
public static final long EXPIRE_10M = 10 * 60; // 10分钟
public static final long EXPIRE_5M = 5 * 60; // 5分钟
public static final long EXPIRE_1M = 60; // 1分钟
}
3.3 创建缓存服务类
文件:src/main/java/com/husky/common/service/CacheService.java
package com.husky.common.service;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.husky.common.core.redis.RedisCache;
import com.husky.common.constant.CacheConstants;
/**
* 缓存服务
* 提供统一的缓存操作接口
*
* @author husky
*/
@Service
public class CacheService
{
@Autowired
private RedisCache redisCache;
/**
* 获取缓存对象,如果不存在则从数据源获取并缓存
*
* @param key 缓存键
* @param expireSeconds 过期时间(秒)
* @param dataLoader 数据加载器
* @return 缓存对象
*/
public <T> T getOrLoad(String key, long expireSeconds, Supplier<T> dataLoader)
{
// 先从缓存获取
T cachedData = redisCache.getCacheObject(key);
if (cachedData != null) {
return cachedData;
}
// 缓存不存在,从数据源加载
T data = dataLoader.get();
if (data != null) {
// 存入缓存
redisCache.setCacheObject(key, data, (int) expireSeconds, TimeUnit.SECONDS);
}
return data;
}
/**
* 获取列表缓存,如果不存在则从数据源获取并缓存
*
* @param key 缓存键
* @param expireSeconds 过期时间(秒)
* @param dataLoader 数据加载器
* @return 缓存列表
*/
public <T> List<T> getListOrLoad(String key, long expireSeconds, Supplier<List<T>> dataLoader)
{
// 先从缓存获取
List<T> cachedList = redisCache.getCacheList(key);
if (cachedList != null && !cachedList.isEmpty()) {
return cachedList;
}
// 缓存不存在,从数据源加载
List<T> list = dataLoader.get();
if (list != null && !list.isEmpty()) {
// 存入缓存
redisCache.setCacheList(key, list);
redisCache.expire(key, expireSeconds);
}
return list;
}
/**
* 设置缓存对象
*
* @param key 缓存键
* @param value 缓存值
* @param expireSeconds 过期时间(秒)
*/
public <T> void set(String key, T value, long expireSeconds)
{
redisCache.setCacheObject(key, value, (int) expireSeconds, TimeUnit.SECONDS);
}
/**
* 删除缓存
*
* @param key 缓存键
*/
public void delete(String key)
{
redisCache.deleteObject(key);
}
/**
* 批量删除缓存(支持通配符)
*
* @param pattern 缓存键模式
*/
public void deletePattern(String pattern)
{
Collection<String> keys = redisCache.keys(pattern);
if (keys != null && !keys.isEmpty()) {
redisCache.deleteObject(keys);
}
}
/**
* 删除客户相关缓存
*
* @param customerId 客户ID
*/
public void deleteCustomerCache(Long customerId)
{
// 删除客户详情缓存
delete(CacheConstants.CUSTOMER_DETAIL_KEY + customerId);
// 删除客户列表缓存
deletePattern(CacheConstants.CUSTOMER_LIST_KEY + "*");
}
/**
* 删除项目相关缓存
*
* @param projectId 项目ID
*/
public void deleteProjectCache(Long projectId)
{
delete(CacheConstants.PROJECT_DETAIL_KEY + projectId);
deletePattern(CacheConstants.PROJECT_LIST_KEY + "*");
}
/**
* 删除设备相关缓存
*
* @param equipmentId 设备ID
*/
public void deleteEquipmentCache(Long equipmentId)
{
delete(CacheConstants.EQUIPMENT_DETAIL_KEY + equipmentId);
deletePattern(CacheConstants.EQUIPMENT_LIST_KEY + "*");
}
/**
* 删除工单相关缓存
*
* @param orderId 工单ID
*/
public void deleteWorkOrderCache(Long orderId)
{
delete(CacheConstants.WORKORDER_DETAIL_KEY + orderId);
deletePattern(CacheConstants.WORKORDER_LIST_KEY + "*");
deletePattern(CacheConstants.WORKORDER_STATS_KEY + "*");
deletePattern(CacheConstants.SUBTASK_LIST_KEY + orderId + ":*");
}
/**
* 删除子任务相关缓存
*
* @param subtaskId 子任务ID
* @param orderId 工单ID
*/
public void deleteSubtaskCache(Long subtaskId, Long orderId)
{
delete(CacheConstants.SUBTASK_DETAIL_KEY + subtaskId);
if (orderId != null) {
delete(CacheConstants.SUBTASK_LIST_KEY + orderId);
// 工单状态可能变化,删除工单缓存
deleteWorkOrderCache(orderId);
}
}
/**
* 删除统计数据缓存
*/
public void deleteStatsCache()
{
deletePattern(CacheConstants.STATS_KEY + "*");
}
/**
* 刷新用户权限缓存
*
* @param userId 用户ID
*/
public void deleteUserPermissionCache(Long userId)
{
deletePattern(CacheConstants.USER_PERMISSION_KEY + userId + ":*");
}
}
3.4 缓存应用示例
示例1:客户信息缓存
文件:BzCustomerServiceImpl.java
@Service
public class BzCustomerServiceImpl implements IBzCustomerService
{
@Autowired
private BzCustomerMapper bzCustomerMapper;
@Autowired
private CacheService cacheService;
/**
* 查询客户详情(带缓存)
*/
@Override
public BzCustomer selectBzCustomerByCustomerId(Long customerId)
{
String cacheKey = CacheConstants.CUSTOMER_DETAIL_KEY + customerId;
return cacheService.getOrLoad(
cacheKey,
CacheConstants.EXPIRE_24H,
() -> bzCustomerMapper.selectBzCustomerByCustomerId(customerId)
);
}
/**
* 查询客户列表(带缓存)
*/
@Override
@DataScope(customerAlias = "bz_customer")
public List<BzCustomer> selectBzCustomerList(BzCustomer bzCustomer)
{
// 列表缓存需要考虑查询条件
// 简化处理:只缓存无条件的全量列表
if (isEmptyQuery(bzCustomer)) {
String cacheKey = CacheConstants.CUSTOMER_LIST_KEY;
return cacheService.getListOrLoad(
cacheKey,
CacheConstants.EXPIRE_1H,
() -> bzCustomerMapper.selectBzCustomerList(bzCustomer)
);
}
// 有查询条件的不缓存,直接查询数据库
return bzCustomerMapper.selectBzCustomerList(bzCustomer);
}
/**
* 新增客户
*/
@Override
public int insertBzCustomer(BzCustomer bzCustomer)
{
int result = bzCustomerMapper.insertBzCustomer(bzCustomer);
// 清除列表缓存
if (result > 0) {
cacheService.deleteCustomerCache(null);
}
return result;
}
/**
* 修改客户
*/
@Override
public int updateBzCustomer(BzCustomer bzCustomer)
{
int result = bzCustomerMapper.updateBzCustomer(bzCustomer);
// 清除相关缓存
if (result > 0) {
cacheService.deleteCustomerCache(bzCustomer.getCustomerId());
}
return result;
}
/**
* 删除客户
*/
@Override
public int deleteBzCustomerByCustomerId(Long customerId)
{
int result = bzCustomerMapper.deleteBzCustomerByCustomerId(customerId);
// 清除相关缓存
if (result > 0) {
cacheService.deleteCustomerCache(customerId);
}
return result;
}
/**
* 判断是否为空查询条件
*/
private boolean isEmptyQuery(BzCustomer bzCustomer)
{
return bzCustomer.getCustomerName() == null
&& bzCustomer.getContactPerson() == null
&& bzCustomer.getContactPhone() == null;
}
}
示例2:统计数据缓存
文件:BzStatisticsServiceImpl.java
@Service
public class BzStatisticsServiceImpl implements IBzStatisticsService
{
@Autowired
private CacheService cacheService;
/**
* 获取Dashboard统计数据(带缓存)
*/
@Override
public Map<String, Object> getDashboardStats()
{
String cacheKey = CacheConstants.STATS_DASHBOARD_KEY;
return cacheService.getOrLoad(
cacheKey,
CacheConstants.EXPIRE_30M,
() -> calculateDashboardStats()
);
}
/**
* 获取工单统计数据(带缓存)
*/
@Override
public Map<String, Object> getWorkOrderStats(String dateRange)
{
String cacheKey = CacheConstants.STATS_WORKORDER_KEY + dateRange;
return cacheService.getOrLoad(
cacheKey,
CacheConstants.EXPIRE_30M,
() -> calculateWorkOrderStats(dateRange)
);
}
/**
* 刷新统计缓存(定时任务调用)
*/
@Scheduled(cron = "0 */30 * * * ?") // 每30分钟执行一次
public void refreshStatsCache()
{
log.info("开始刷新统计数据缓存...");
// 删除旧缓存
cacheService.deleteStatsCache();
// 预热常用统计数据
getDashboardStats();
getWorkOrderStats("today");
getWorkOrderStats("week");
getWorkOrderStats("month");
log.info("统计数据缓存刷新完成");
}
}
示例3:检查模板缓存
文件:BzCheckTemplateServiceImpl.java
@Service
public class BzCheckTemplateServiceImpl implements IBzCheckTemplateService
{
@Autowired
private CacheService cacheService;
/**
* 查询检查模板详情(带缓存)
*/
@Override
public BzCheckTemplate selectBzCheckTemplateByTemplateId(Long templateId)
{
String cacheKey = CacheConstants.TEMPLATE_DETAIL_KEY + templateId;
return cacheService.getOrLoad(
cacheKey,
CacheConstants.EXPIRE_24H, // 模板变化较少,缓存24小时
() -> bzCheckTemplateMapper.selectBzCheckTemplateByTemplateId(templateId)
);
}
/**
* 查询模板列表(带缓存)
*/
@Override
public List<BzCheckTemplate> selectBzCheckTemplateList(BzCheckTemplate bzCheckTemplate)
{
if (isEmptyQuery(bzCheckTemplate)) {
String cacheKey = CacheConstants.TEMPLATE_LIST_KEY;
return cacheService.getListOrLoad(
cacheKey,
CacheConstants.EXPIRE_24H,
() -> bzCheckTemplateMapper.selectBzCheckTemplateList(bzCheckTemplate)
);
}
return bzCheckTemplateMapper.selectBzCheckTemplateList(bzCheckTemplate);
}
}
四、高级优化策略
4.1 缓存预热
在系统启动时预加载热点数据:
文件:src/main/java/com/husky/framework/config/CacheWarmUpRunner.java
package com.husky.framework.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.husky.business.service.IBzCustomerService;
import com.husky.business.service.IBzCheckTemplateService;
import com.husky.business.service.IBzTechnicianService;
/**
* 缓存预热
* 在应用启动时预加载热点数据到缓存
*
* @author husky
*/
@Component
public class CacheWarmUpRunner implements CommandLineRunner
{
private static final Logger log = LoggerFactory.getLogger(CacheWarmUpRunner.class);
@Autowired
private IBzCustomerService customerService;
@Autowired
private IBzCheckTemplateService templateService;
@Autowired
private IBzTechnicianService technicianService;
@Override
public void run(String... args) throws Exception
{
log.info("开始缓存预热...");
try {
// 预加载客户列表
customerService.selectBzCustomerList(new BzCustomer());
log.info("客户列表缓存预热完成");
// 预加载检查模板
templateService.selectBzCheckTemplateList(new BzCheckTemplate());
log.info("检查模板缓存预热完成");
// 预加载技术员列表
technicianService.selectBzTechnicianList(new BzTechnician());
log.info("技术员列表缓存预热完成");
log.info("缓存预热完成");
} catch (Exception e) {
log.error("缓存预热失败", e);
}
}
}
4.2 缓存穿透防护
使用布隆过滤器或空值缓存防止缓存穿透:
/**
* 查询对象(防止缓存穿透)
*/
public <T> T getOrLoad(String key, long expireSeconds, Supplier<T> dataLoader)
{
// 先从缓存获取
T cachedData = redisCache.getCacheObject(key);
if (cachedData != null) {
// 检查是否为空对象标记
if (cachedData instanceof NullValue) {
return null;
}
return cachedData;
}
// 缓存不存在,从数据源加载
T data = dataLoader.get();
if (data != null) {
// 存入缓存
redisCache.setCacheObject(key, data, (int) expireSeconds, TimeUnit.SECONDS);
} else {
// 数据不存在,缓存空对象(较短时间)防止缓存穿透
redisCache.setCacheObject(key, new NullValue(), 5 * 60, TimeUnit.SECONDS);
}
return data;
}
/**
* 空值标记类
*/
class NullValue implements Serializable {
private static final long serialVersionUID = 1L;
}
4.3 缓存雪崩防护
为缓存键添加随机过期时间:
/**
* 设置缓存(添加随机过期时间防止雪崩)
*/
public <T> void setWithRandomExpire(String key, T value, long expireSeconds)
{
// 添加 0-300 秒的随机时间
long randomExpire = expireSeconds + new Random().nextInt(300);
redisCache.setCacheObject(key, value, (int) randomExpire, TimeUnit.SECONDS);
}
4.4 缓存更新策略
策略选择:
| 场景 | 策略 | 说明 |
|---|---|---|
| 数据变更频繁 | Cache-Aside | 更新数据库后删除缓存 |
| 数据读多写少 | Read-Through | 缓存未命中时自动加载 |
| 需要强一致性 | Write-Through | 同时更新数据库和缓存 |
| 统计数据 | 定时刷新 | 定时任务更新缓存 |
五、监控和运维
5.1 缓存监控指标
需要关注的指标:
/**
* 缓存统计信息
*/
public class CacheStats {
private long hitCount; // 命中次数
private long missCount; // 未命中次数
private double hitRate; // 命中率
private long cacheSize; // 缓存大小
private long evictions; // 淘汰次数
}
5.2 缓存管理接口
Controller:CacheController.java
@RestController
@RequestMapping("/monitor/cache")
public class CacheController
{
@Autowired
private CacheService cacheService;
/**
* 清空指定模块缓存
*/
@PreAuthorize("@ss.hasPermi('monitor:cache:clear')")
@DeleteMapping("/clear/{module}")
public AjaxResult clearCache(@PathVariable String module)
{
switch (module) {
case "customer":
cacheService.deletePattern(CacheConstants.CUSTOMER_KEY + "*");
break;
case "workorder":
cacheService.deletePattern(CacheConstants.WORKORDER_KEY + "*");
break;
case "stats":
cacheService.deleteStatsCache();
break;
case "all":
cacheService.deletePattern(CacheConstants.CACHE_PREFIX + "*");
break;
default:
return AjaxResult.error("未知的模块: " + module);
}
return AjaxResult.success("缓存清理成功");
}
/**
* 获取缓存统计信息
*/
@PreAuthorize("@ss.hasPermi('monitor:cache:query')")
@GetMapping("/stats")
public AjaxResult getCacheStats()
{
// 实现缓存统计逻辑
return AjaxResult.success();
}
}
六、实施步骤
阶段一:基础设施(第1周)
-
- 调整 Redis 连接池配置
-
- 创建
CacheConstants常量类
- 创建
-
- 创建
CacheService缓存服务类
- 创建
-
- 编写单元测试验证缓存服务
阶段二:核心业务缓存(第2-3周)
-
- 实现客户信息缓存
-
- 实现设备信息缓存
-
- 实现检查模板缓存
-
- 实现技术员信息缓存
-
- 实现字典数据缓存
阶段三:业务场景缓存(第4-5周)
-
- 实现工单列表缓存
-
- 实现统计数据缓存
-
- 实现用户权限缓存
-
- 添加缓存预热功能
阶段四:优化和监控(第6周)
-
- 实现缓存穿透防护
-
- 实现缓存雪崩防护
-
- 添加缓存监控接口
-
- 性能测试和优化
七、预期效果
7.1 性能提升
| 场景 | 优化前 | 优化后 | 提升比例 |
|---|---|---|---|
| 客户列表查询 | 200ms | 10ms | 95% ↑ |
| 工单详情查询 | 150ms | 5ms | 97% ↑ |
| Dashboard统计 | 800ms | 50ms | 94% ↑ |
| 设备列表查询 | 180ms | 8ms | 96% ↑ |
7.2 数据库压力
- 数据库查询次数减少:70-90%
- 数据库连接数降低:50-70%
- 系统整体响应时间:提升 60-80%
八、注意事项
8.1 缓存一致性
⚠️ 重要:
- 更新数据库后必须删除相关缓存
- 涉及多表关联的数据要一起删除
- 工单状态变化要同步更新统计缓存
8.2 内存管理
⚠️ 监控:
- Redis 内存使用率
- 设置合理的过期时间
- 定期清理无用缓存
8.3 缓存键设计
✅ 最佳实践:
- 使用统一的命名规范
- 避免使用过长的键名
- 键名要有业务含义
九、常见问题
Q1:什么时候不应该使用缓存?
A:以下场景不建议使用缓存:
- 实时性要求极高的数据(如实时位置、实时库存)
- 数据变化极其频繁(如秒杀库存)
- 数据量极大的列表(建议分页缓存)
Q2:缓存更新失败怎么办?
A:
- 使用异步方式删除缓存,失败重试
- 设置合理的过期时间,即使删除失败也会自动过期
- 记录日志,人工介入
Q3:如何测试缓存效果?
A:
- 使用 JMeter 进行压力测试
- 对比开启/关闭缓存的响应时间
- 监控 Redis 命中率
- 监控数据库查询次数
十、总结
本方案通过以下方式提升系统性能:
- ✅ 多层缓存架构 - Redis 分布式缓存 + 可选本地缓存
- ✅ 统一缓存服务 -
CacheService提供标准化缓存操作 - ✅ 合理过期策略 - 根据数据特点设置不同过期时间
- ✅ 缓存预热 - 启动时预加载热点数据
- ✅ 防护机制 - 缓存穿透、雪崩防护
- ✅ 监控管理 - 缓存监控和管理接口
文档维护:随缓存策略变更同步更新
最后更新:2025-10-27
作者:聂盼盼 创建时间:2025-10-27 17:19
最后编辑:聂盼盼 更新时间:2025-10-28 19:53
最后编辑:聂盼盼 更新时间:2025-10-28 19:53