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 缓存管理接口

ControllerCacheController.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周)

    1. 调整 Redis 连接池配置
    1. 创建 CacheConstants 常量类
    1. 创建 CacheService 缓存服务类
    1. 编写单元测试验证缓存服务

阶段二:核心业务缓存(第2-3周)

    1. 实现客户信息缓存
    1. 实现设备信息缓存
    1. 实现检查模板缓存
    1. 实现技术员信息缓存
    1. 实现字典数据缓存

阶段三:业务场景缓存(第4-5周)

    1. 实现工单列表缓存
    1. 实现统计数据缓存
    1. 实现用户权限缓存
    1. 添加缓存预热功能

阶段四:优化和监控(第6周)

    1. 实现缓存穿透防护
    1. 实现缓存雪崩防护
    1. 添加缓存监控接口
    1. 性能测试和优化

七、预期效果

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

  1. 使用异步方式删除缓存,失败重试
  2. 设置合理的过期时间,即使删除失败也会自动过期
  3. 记录日志,人工介入

Q3:如何测试缓存效果?

A

  1. 使用 JMeter 进行压力测试
  2. 对比开启/关闭缓存的响应时间
  3. 监控 Redis 命中率
  4. 监控数据库查询次数

十、总结

本方案通过以下方式提升系统性能:

  1. 多层缓存架构 - Redis 分布式缓存 + 可选本地缓存
  2. 统一缓存服务 - CacheService 提供标准化缓存操作
  3. 合理过期策略 - 根据数据特点设置不同过期时间
  4. 缓存预热 - 启动时预加载热点数据
  5. 防护机制 - 缓存穿透、雪崩防护
  6. 监控管理 - 缓存监控和管理接口

文档维护:随缓存策略变更同步更新
最后更新:2025-10-27

作者:聂盼盼  创建时间:2025-10-27 17:19
最后编辑:聂盼盼  更新时间:2025-10-28 19:53