You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
13 KiB
13 KiB
缓存策略
**本文引用的文件** - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java) - [SysDeptServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysDeptServiceImpl.java) - [application.yml](file://crm-app/src/main/resources/application.yml) - [application.yml](file://crm-auth/src/main/resources/application.yml)目录
简介
本文件围绕CRM后端项目的缓存策略进行系统化说明,重点覆盖:
- Redis缓存配置与使用(连接池、序列化、集群模式)
- 常见缓存设计模式(Cache-Aside、Read-Through、Write-Through)的适用场景与实现要点
- 以 DeptTreeCache 为例,展示树形结构数据的缓存优化方案
- 缓存一致性保障(更新删除、失效时间、分布式锁等)
- 缓存监控、性能调优与故障恢复最佳实践
项目结构
本项目采用多模块组织,缓存相关能力主要分布在基础模块与认证模块中:
- crm-base:提供通用配置与工具,包含 Redis 配置类
- crm-auth:业务服务,包含部门树缓存实现与调用方
graph TB
subgraph "应用层"
APP["CrmAppApplication"]
AUTH_APP["AuthApplication"]
end
subgraph "基础模块 crm-base"
REDIS_CFG["RedisConfig<br/>Redis连接/序列化/集群配置"]
end
subgraph "认证模块 crm-auth"
DEPT_CACHE["DeptTreeCache<br/>部门树缓存封装"]
DEPT_SVC["SysDeptServiceImpl<br/>部门服务实现"]
end
subgraph "外部依赖"
REDIS["Redis 服务器/集群"]
DB["数据库(MyBatis-Plus)"]
end
APP --> AUTH_APP
AUTH_APP --> DEPT_SVC
DEPT_SVC --> DEPT_CACHE
DEPT_CACHE --> REDIS
DEPT_SVC --> DB
REDIS_CFG -.-> DEPT_CACHE
图示来源
- RedisConfig.java
- DeptTreeCache.java
- SysDeptServiceImpl.java
- application.yml
- application.yml
章节来源
- RedisConfig.java
- DeptTreeCache.java
- SysDeptServiceImpl.java
- application.yml
- application.yml
核心组件
- RedisConfig:集中管理Redis连接、序列化、超时、集群等关键参数,为上层缓存组件提供统一访问能力。
- DeptTreeCache:面向部门树的专用缓存封装,提供读路径优先、按需加载、批量构建与失效控制。
- SysDeptServiceImpl:部门数据服务,负责在写操作后触发缓存失效或重建,保证读写一致性。
章节来源
- RedisConfig.java
- DeptTreeCache.java
- SysDeptServiceImpl.java
架构总览
下图展示了“服务-缓存-存储”的整体交互流程,以及不同缓存模式下的数据流向。
sequenceDiagram
participant Client as "客户端"
participant Service as "SysDeptServiceImpl"
participant Cache as "DeptTreeCache"
participant Redis as "Redis"
participant DB as "数据库"
Note over Client,Service : "读路径(示例)"
Client->>Service : "查询部门树"
Service->>Cache : "getTree()"
alt "命中缓存"
Cache-->>Service : "返回树节点"
Service-->>Client : "返回结果"
else "未命中"
Cache->>DB : "加载根节点/全量数据"
DB-->>Cache : "原始数据"
Cache->>Redis : "序列化并写入"
Redis-->>Cache : "OK"
Cache-->>Service : "返回树节点"
Service-->>Client : "返回结果"
end
Note over Client,Service : "写路径(示例)"
Client->>Service : "修改部门信息"
Service->>DB : "持久化变更"
Service->>Cache : "invalidate()/refresh()"
Cache->>Redis : "删除/更新键"
Redis-->>Cache : "OK"
Service-->>Client : "返回成功"
图示来源
- DeptTreeCache.java
- SysDeptServiceImpl.java
详细组件分析
Redis 配置与使用
- 连接池与超时:通过连接池参数控制最大连接数、空闲连接、获取超时等,避免连接耗尽与阻塞。
- 序列化策略:建议统一使用JSON序列化,确保跨语言兼容与可读性;对热点对象可考虑二进制压缩以提升吞吐。
- 集群模式:支持单机、哨兵与集群三种部署形态,需根据容量与可用性选择;集群模式下注意Key哈希槽分布与跨槽事务限制。
- Key命名规范:按“模块:实体:维度”分层,便于定位与批量清理;树形结构可采用“dept:tree:root”作为根键。
- 过期与淘汰:设置合理TTL并结合业务生命周期;热点数据可延长TTL,冷数据缩短TTL。
章节来源
- RedisConfig.java
- application.yml
- application.yml
缓存设计模式
- Cache-Aside(旁路缓存)
- 适用场景:读多写少、允许短暂不一致、快速上线
- 实现要点:读时先查缓存,未命中再查库并回填;写时先更新库,再删除缓存
- 风险点:并发写导致脏读,可通过分布式锁或版本号解决
- Read-Through(读穿透)
- 适用场景:希望缓存层透明化,简化调用方逻辑
- 实现要点:缓存代理在缺失时自动从数据源加载并回填
- 风险点:数据源异常影响读取链路,需要降级与熔断
- Write-Through(写穿透)
- 适用场景:强一致要求高、写路径可控
- 实现要点:写时同步更新缓存与数据源,失败回滚
- 风险点:写延迟增加,需评估吞吐与一致性权衡
[本节为概念性内容,不直接分析具体文件]
树形结构缓存优化:DeptTreeCache
- 目标:将部门树结构整体缓存,减少重复组装与DB查询压力
- 数据结构:以根节点为键,子节点列表递归构建;必要时拆分“根键+子键集合”提升局部刷新效率
- 读路径:优先命中缓存,未命中则从DB加载并按层级拼装,最后落盘
- 写路径:部门增删改后,删除根键或局部子键,下次读时懒加载重建
- 并发控制:首次加载时使用分布式锁防击穿;热点更新使用短锁或版本号防雪崩
- 失效策略:基于事件驱动(写操作发布失效消息)或定时任务校验一致性
flowchart TD
Start(["进入 getTree"]) --> CheckCache["检查根键是否存在"]
CheckCache --> |存在| ReturnCache["返回缓存树"]
CheckCache --> |不存在| AcquireLock["尝试获取分布式锁"]
AcquireLock --> LockOk{"是否获得锁"}
LockOk --> |否| Fallback["等待或降级返回空/旧值"]
LockOk --> |是| LoadFromDB["从数据库加载部门数据"]
LoadFromDB --> BuildTree["构建部门树结构"]
BuildTree --> SaveCache["序列化并写入Redis"]
SaveCache --> ReleaseLock["释放分布式锁"]
ReleaseLock --> ReturnResult["返回树结构"]
Fallback --> End(["结束"])
ReturnCache --> End
ReturnResult --> End
图示来源
- DeptTreeCache.java
章节来源
- DeptTreeCache.java
服务层集成:SysDeptServiceImpl
- 读接口:委托 DeptTreeCache 获取树结构,屏蔽底层细节
- 写接口:在完成数据库变更后,调用缓存失效或刷新方法,确保后续读路径能拿到最新数据
- 事务边界:写操作与缓存失效在同一事务内协调,避免中间态暴露
sequenceDiagram
participant Client as "客户端"
participant Service as "SysDeptServiceImpl"
participant DB as "数据库"
participant Cache as "DeptTreeCache"
participant Redis as "Redis"
Client->>Service : "更新部门信息"
Service->>DB : "执行更新"
DB-->>Service : "返回成功"
Service->>Cache : "invalidateRoot()"
Cache->>Redis : "删除根键"
Redis-->>Cache : "OK"
Service-->>Client : "返回成功"
图示来源
- SysDeptServiceImpl.java
- DeptTreeCache.java
章节来源
- SysDeptServiceImpl.java
- DeptTreeCache.java
依赖关系分析
- 模块耦合
- DeptTreeCache 依赖 RedisConfig 提供的RedisTemplate/ConnectionFactory
- SysDeptServiceImpl 依赖 DeptTreeCache 与 MyBatis-Plus 数据访问层
- 外部依赖
- Redis:承担热点数据缓存与共享状态
- 数据库:权威数据源,缓存仅作为加速层
graph LR
Svc["SysDeptServiceImpl"] --> Cache["DeptTreeCache"]
Cache --> RedisCfg["RedisConfig"]
Cache --> RedisNode["Redis"]
Svc --> DB["数据库"]
图示来源
- SysDeptServiceImpl.java
- DeptTreeCache.java
- RedisConfig.java
章节来源
- SysDeptServiceImpl.java
- DeptTreeCache.java
- RedisConfig.java
性能考虑
- 连接池调优
- 根据QPS与RT估算连接数,避免过大导致内存占用过高
- 调整获取连接超时,防止线程堆积
- 序列化优化
- 热点对象使用紧凑格式(如Protobuf/Kryo),非热点使用JSON
- 大对象分片存储,避免单次序列化开销过大
- 缓存粒度
- 树形结构可按层级拆分键,提高局部更新效率
- 热点键打散,避免单键过大
- 过期与预热
- 启动时预热常用树结构,降低冷启动冲击
- 动态TTL结合访问频率调整
- 集群与分片
- 关注Key哈希分布,避免热点槽
- 跨槽操作尽量规避,必要时拆分为多次请求
[本节为通用指导,不直接分析具体文件]
故障排查指南
- 常见问题
- 缓存穿透:空值缓存+布隆过滤器限流
- 缓存击穿:分布式锁保护首次加载
- 缓存雪崩:随机TTL+多级缓存
- 数据不一致:写后删/版本号/双写一致性校验
- 监控指标
- 命中率、平均RT、P99/P95、错误率、连接池使用率
- Redis内存、CPU、网络IO、慢查询日志
- 恢复策略
- 快速降级:关闭缓存直连DB
- 灰度发布:逐步放量新缓存策略
- 回滚机制:保留旧版本键与快照
[本节为通用指导,不直接分析具体文件]
结论
- 通过统一的Redis配置与规范的Key设计,奠定稳定高效的缓存基础
- 选择合适的缓存模式(Cache-Aside为主,必要时引入Read/Write-Through)平衡一致性与性能
- 针对树形结构采用专用缓存封装,显著提升读性能与可维护性
- 完善的一致性策略与监控体系,保障系统在高可用与高性能之间取得平衡
[本节为总结性内容,不直接分析具体文件]
附录
- 配置清单建议
- 连接池:最大连接数、空闲超时、获取超时
- 序列化:默认JSON,热点对象可选二进制
- 集群:节点地址、密码、超时、重试策略
- Key前缀:模块:实体:维度
- TTL:按业务设定,热点适当延长
- 代码片段路径
- Redis配置入口:RedisConfig.java
- 树缓存封装:DeptTreeCache.java
- 服务集成点:SysDeptServiceImpl.java
- 应用配置:application.yml, application.yml