本地缓存 vs 远程缓存:优缺点及使用场景

做 Android 构建优化的朋友,迟早都会面对一个问题:到底该用本地缓存还是远程缓存?

我以前也纠结过。有一回项目从几十个模块膨胀到一百多,本地缓存经常撑爆磁盘,团队里大家各自跑构建,缓存完全没法共享。后来引入远程缓存,结果又遇到网络抖动导致构建变慢。折腾一圈下来,我才真正摸清这两兄弟的脾气。

今天我就把它们的优缺点和使用场景掰开揉碎了讲。你想想看,搞懂了这些,以后选方案就不会再“拍脑袋”了。

构建缓存决策体系 构建缓存决策 本地缓存 ✅ 速度极快 无网络依赖 ❌ 磁盘占用大 跨机器不可共享 远程缓存 ✅ 团队共享 CI/CD 友好 ❌ 网络延迟 存储成本 如何选择? • 单机开发 → 本地缓存 • 团队协作/CI → 优先远程缓存 • 最佳实践:本地缓存 + 远程缓存混合使用

本地缓存:快,但孤独

本地缓存就是把构建产物(比如 class 文件、dex、资源包)存在你电脑的 ~/.gradle/caches 里。AGP 默认就开着。

它的优点很直接:快。没有网络开销,每次读缓存就是磁盘 I/O,SSD 上基本毫秒级响应。我记得有个老兵项目,第一次全量编译要 10 分钟,启用本地缓存后,如果只改了一行 Java,增量编译 20 秒就搞定。

但它也有明显的短板——跨机器不共享。你想想看,团队里 10 个人,每个人都在本地生成一份同样的缓存,浪费磁盘不说,新同学拉完代码第一次构建还得从头编译。另一个问题是磁盘占用,我见过有人 .gradle 目录膨胀到 50GB,直接撑爆 C 盘。

适用场景:

  • 个人开发机,每次只改少量代码
  • 网络不稳定或离线办公环境
  • 小项目(几十个模块以内)

远程缓存:共享,但有代价

远程缓存把构建产物上传到远端存储(比如 HTTP 服务器、S3、Nexus 等),其他机器可以下载复用。常用方案是 build-cache-http 或者自定义存储后端。

它最大的价值是团队共享。举个例子:CI 机器上午全量构建过一次,下午开发者拉代码后只需要下载缓存,增量编译时间从 8 分钟降到 1 分钟。我在项目中多次见到,远程缓存让团队整体的构建命中率从 20% 提升到 70% 以上。

但远程缓存也有坑:

  • 网络延迟:上传和下载都要走网络,如果带宽不够或者服务器在国外,反而比本地编译还慢。
  • 存储成本:需要运维维护后端,清理过期缓存也是麻烦事。
  • 缓存失效风险:如果 AGP 升级或者构建脚本变动,所有远程缓存可能全部失效,这个过程很痛苦。

我做过一个实验:50MB 的构建产物,局域网远程缓存下载耗时约 3 秒;但如果网络抖动,重试两三次就十几秒过去了。所以千万别盲目上远程缓存。

避坑指南:

我曾经遇到过一个事故:团队把远程缓存直接接到对象存储上,结果某个模块输出包含了路径,导致不同机器的缓存互相覆盖,构建时随机报错。排查了两天才找到原因。记住:远程缓存必须保证产物 key 与平台无关,使用相对路径或者哈希生成 key。

核心对比一览

维度 本地缓存 远程缓存
读写速度 极快(磁盘 I/O) 受网络延迟影响
共享能力 仅本机 全团队/CI
磁盘占用 每台机器独立占用 服务端统一存储
运维成本 零 需部署和清理策略
缓存命中率 高(但只对本机先前的构建) 高(团队共享,但受版本影响)
适用项目规模 小到中型 中到大型团队

使用场景归纳

实际项目中,我通常按以下几类情况来决策:

  1. 个人日常开发:肯定优先本地缓存。你想想,改几行代码就要上传下载,何必呢。
  2. CI/CD 持续集成:必须上远程缓存。每次构建都是全新环境,没有远程共享的话,CI 中的增量编译就是个笑话。我个人习惯把远程缓存和 CI 构建任务深度绑定,比如只在 release 分支全量构建后产生缓存,其他分支直接复用。
  3. 大型团队多人协作:建议本地缓存 + 远程缓存同时开启。AGP 默认会先查本地,如果 hit 则直接使用,miss 了再查远程。这个组合最实用。
  4. 网络受限环境:只能本地缓存。你可以考虑在公司内网搭建远程缓存服务器,降低延迟。

我的一个技巧:

在 gradle.properties 中可以通过 org.gradle.caching=true 开启缓存。如果你想同时用两种缓存,需要配置 buildCache 块:

// settings.gradle.kts
buildCache {
    local {
        isEnabled = true
        directory = File(rootDir, ".build-cache")
        // 设置最大大小
        removeUnusedEntriesAfterDays = 7
    }
    remote(HttpBuildCache::class) {
        url = uri("http://your-cache-server/cache/")
        isPush = true
        // 设置该远程缓存只存储关键构建产物
    }
}

最后说两句

本地缓存和远程缓存不是二选一的关系,更像是“搭档”。我的经验是:小项目只开本地,大项目一定混用。但别忘了定期清理缓存,特别是本地缓存,我见过有人积累了 2 年前的缓存,重建后速度反而变慢。嗯,今天就聊这么多,你先消化这些,下次我们聊聊如何监控缓存命中率,那可是优化效果的“照妖镜”。