7.1 KiB
7.1 KiB
🚀 文件下载缓存机制说明
📋 功能概述
为了提高文件下载性能,减少对MinIO存储的重复请求,系统实现了基于内存的文件缓存机制。该机制具有以下特性:
✨ 核心特性
- LRU淘汰策略: 当缓存空间不足时,自动淘汰最久未访问的文件
- 智能大小控制: 只缓存小于50MB的文件,避免内存溢出
- 过期时间管理: 缓存条目具有30分钟的有效期
- 并发安全: 使用读写锁保证多线程环境下的数据一致性
- 统计监控: 提供缓存命中率、大小等统计信息
- 配置化管理: 所有缓存参数可通过配置文件调整
🔧 配置参数
基础配置
在 application.yml 或 application-dev.yml 中添加以下配置:
system:
file-cache:
# 是否启用文件缓存
enabled: true
# 最大缓存文件数量
max-cache-size: 100
# 最大缓存大小(字节)- 500MB
max-cache-size-bytes: 524288000
# 缓存过期时间(毫秒)- 30分钟
expire-time-ms: 1800000
# 最大缓存单个文件大小(字节)- 50MB
max-single-file-size: 52428800
# LRU淘汰比例(百分比)
evict-ratio: 20
# 是否启用缓存统计
enable-stats: true
# 缓存清理间隔(毫秒)- 5分钟
cleanup-interval-ms: 300000
参数说明
| 参数名 | 默认值 | 说明 |
|---|---|---|
enabled |
true |
是否启用文件缓存功能 |
max-cache-size |
100 |
缓存的最大文件数量 |
max-cache-size-bytes |
524288000 |
缓存的最大总大小(500MB) |
expire-time-ms |
1800000 |
缓存条目的过期时间(30分钟) |
max-single-file-size |
52428800 |
单个文件的最大缓存大小(50MB) |
evict-ratio |
20 |
LRU淘汰时移除的条目比例(20%) |
enable-stats |
true |
是否启用缓存统计功能 |
cleanup-interval-ms |
300000 |
自动清理过期条目的间隔(5分钟) |
🚀 使用方法
1. 文件下载
缓存机制对用户透明,正常使用文件下载接口即可:
GET /resource/oss/download/{ossId}
系统会自动:
- 检查缓存中是否存在该文件
- 如果存在且未过期,直接从缓存返回
- 如果不存在或已过期,从MinIO下载并缓存
2. 缓存管理API
获取缓存统计信息
GET /resource/oss/cache/stats
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"cacheSize": 25,
"currentCacheSizeBytes": 104857600,
"maxCacheSizeBytes": 524288000,
"cacheHits": 150,
"cacheMisses": 50,
"hitRate": "75.00%"
}
}
清空缓存
DELETE /resource/oss/cache/clear
移除指定文件缓存
DELETE /resource/oss/cache/{ossId}
📊 性能优化
缓存命中率优化
- 增加缓存大小: 对于内存充足的环境,适当增加
max-cache-size-bytes - 调整文件数量: 对于小文件较多的场景,增加
max-cache-size - 优化过期时间: 根据文件访问频率调整
expire-time-ms
内存使用优化
- 控制单个文件大小: 根据实际情况调整
max-single-file-size - 调整淘汰策略: 通过
evict-ratio控制淘汰比例 - 定期清理: 设置合适的
cleanup-interval-ms
监控指标
- 缓存命中率: 目标 > 70%
- 内存使用率: 监控缓存占用的内存大小
- 响应时间: 缓存命中时的下载速度应显著提升
🔍 技术实现
核心组件
1. 缓存条目类 (CacheEntry)
private class CacheEntry {
private final byte[] data; // 文件数据
private final long size; // 文件大小
private final long createTime; // 创建时间
private final long lastAccessTime; // 最后访问时间
private final String contentType; // 内容类型
}
2. LRU淘汰策略
private void evictLRUEntries() {
// 按最后访问时间排序
List<Map.Entry<Long, CacheEntry>> entries = new ArrayList<>(fileCache.entrySet());
entries.sort((e1, e2) -> Long.compare(e1.getValue().getLastAccessTime(), e2.getValue().getLastAccessTime()));
// 淘汰最旧的20%条目
int evictCount = Math.max(1, entries.size() / 5);
for (int i = 0; i < evictCount && i < entries.size(); i++) {
Map.Entry<Long, CacheEntry> entry = entries.get(i);
fileCache.remove(entry.getKey());
currentCacheSize.addAndGet(-entry.getValue().getSize());
}
}
3. 并发安全
- 使用
ConcurrentHashMap存储缓存数据 - 使用
ReentrantReadWriteLock保证读写安全 - 使用
AtomicLong统计缓存大小和命中率
工作流程
-
文件下载请求
用户请求 → 权限验证 → 检查缓存 → 返回文件 -
缓存命中流程
检查缓存 → 验证过期 → 直接返回 → 更新统计 -
缓存未命中流程
检查缓存 → 从MinIO下载 → 添加到缓存 → 返回文件 -
缓存管理流程
检查大小限制 → LRU淘汰 → 清理过期 → 添加新条目
⚠️ 注意事项
1. 内存管理
- 缓存会占用JVM堆内存,需要合理配置缓存大小
- 建议监控应用的内存使用情况
- 大文件不会被缓存,避免内存溢出
2. 数据一致性
- 缓存的文件数据可能与MinIO中的文件不同步
- 如果需要最新数据,可以手动清除缓存
- 建议在文件更新后清除相关缓存
3. 性能考虑
- 缓存命中时响应速度显著提升
- 缓存未命中时会有轻微的性能开销(用于缓存数据)
- 建议根据实际访问模式优化缓存参数
4. 安全考虑
- 缓存数据存储在内存中,应用重启后会丢失
- 敏感文件建议设置较短的过期时间
- 可以通过API手动管理缓存
🔧 故障排查
常见问题
-
缓存命中率低
- 检查缓存大小配置是否合理
- 分析文件访问模式
- 调整过期时间
-
内存使用过高
- 减少缓存大小限制
- 降低单个文件大小限制
- 增加淘汰比例
-
缓存不生效
- 检查
enabled配置是否为true - 确认文件大小是否超过限制
- 查看日志中的缓存相关信息
- 检查
监控建议
-
定期检查缓存统计
curl -X GET "http://localhost:8080/resource/oss/cache/stats" -
监控应用内存使用
jstat -gc <pid> 1000 -
查看缓存相关日志
tail -f logs/application.log | grep "cache"
📈 性能测试
测试场景
- 首次下载: 从MinIO下载并缓存
- 缓存命中: 从内存直接返回
- 并发下载: 多用户同时下载同一文件
- 大文件下载: 超过缓存大小限制的文件
预期性能提升
- 缓存命中时: 响应时间减少 80-90%
- 并发访问: 显著减少MinIO压力
- 整体性能: 平均下载速度提升 60-80%
📝 维护说明: 本文档基于文件缓存机制的实现,如需修改缓存策略或参数,请同步更新相关代码和配置。