mod_cache_disk不适合海量小文件存储优化,它专为反向代理响应缓存设计,缺乏inode管理、自动清理、去重压缩等能力,易因目录文件密度过高导致inode耗尽、I/O性能崩溃及缓存不可控失效。

直接说结论:mod_cache_disk 不适合海量小文件存储优化,它天生为反向代理响应缓存设计,不是通用小文件存储系统;强行用于小文件会迅速耗尽 inode、拖垮 I/O、导致缓存失效不可控。
为什么 mod_cache_disk 无法胜任海量小文件场景
它把每个缓存对象按 URI 哈希后拆成多级子目录存放(如 CacheDirLevels 2 + CacheDirLength 1 生成 a/b/xxx.cache),但哈希不保证均匀,实际运行中极易出现单个子目录堆积数万文件——Linux ext4 默认每目录 inode 限额约 65536,一旦触顶,mkdir 和 open 就开始报 No space left on device,哪怕磁盘剩余空间充足。
更关键的是:mod_cache_disk 没有后台清理线程,不主动删除过期文件;它只在请求命中时检查 Last-Modified 或 Cache-Control,过期文件永远躺在磁盘上,直到你手动跑 htcacheclean。
- 典型症状:缓存目录
df -i显示 inode 使用率 98%,但du -sh只占磁盘 10% - 它不支持自定义键名或批量读写接口,所有存取必须走 HTTP 请求生命周期
- 没有文件去重、压缩、分片机制,相同内容不同 URI 会存多份
CacheDirLevels 和 CacheDirLength 的真实影响
这两个参数控制缓存路径的“扇出结构”,但总乘积不能超过 20(CacheDirLevels * CacheDirLength ≤ 20)。设成 CacheDirLevels 5 + CacheDirLength 4 看似能摊薄文件密度,实际会导致路径过深(a/b/c/d/e/xxx.cache),每次 open 都要 traverse 5 层目录,小文件随机读性能反而暴跌。
生产环境建议保守配置:CacheDirLevels 2 + CacheDirLength 2(共 4 层目录),配合 CacheRoot 挂在 SSD 或独立 RAID 卷上;若真要撑住日均百万级缓存项,必须提前用 find /path -type d | wc -l 监控子目录数量,超过 5000 就该切分 CacheRoot 或换方案。
Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
替代方案:什么情况下该放弃 mod_cache_disk
当你的需求明确包含以下任意一条,就该立刻停手:
- 需要按业务 ID 而非 URI 存取文件(比如查用户头像用
/avatar/12345,但后端实际是GET /api/user/12345/avatar) - 文件大小普遍 mod_cache_disk 默认
CacheMinFileSize 1,但小文件元数据开销远超内容本身) - 要求 99.9% 写入成功率,且不能容忍缓存穿透(它对
5xx响应默认不缓存) - 需要原子性 rename、硬链接共享、或跨机房同步能力
此时应转向专用对象存储:MinIO(私有化部署)、Amazon S3(带 lifecycle policy 自动清理)、甚至轻量级 LiteFS(SQLite 封装的文件系统抽象)——它们提供真正的 PUT/GET 接口、内置 GC、可监控指标全,而 mod_cache_disk 只是 HTTP 流水线里一个被动环节。
如果仍坚持用 mod_cache_disk,必须做的三件事
仅限临时过渡或低 QPS 场景:
- 每天定时执行:
htcacheclean -d -n -p /var/cache/apache2/mod_cache_disk -l 5G(保留最新 5GB,自动剔旧) - 在 Apache 配置中显式关闭干扰项:
CacheIgnoreCacheControl off(别让后端乱设no-store搞崩缓存率)、CacheIgnoreHeaders Set-Cookie Vary(避免因 Cookie 头跳过缓存) - 用
inotifywait监控CacheRoot下子目录创建事件,一旦某目录find . -maxdepth 1 -type f | wc -l超过 10000,立即告警并切到备用CacheRoot
最易被忽略的一点:mod_cache_disk 的缓存键只含 URI 和 Host 头,不包含 Query String 参数顺序、Accept-Encoding 差异或请求体哈希——这意味着 /api?sort=asc&page=1 和 /api?page=1&sort=asc 会被视为两个缓存项,小文件数量会指数级膨胀。别指望它替你做键归一化。










