go build重复下载模块的根本原因是工具链隐式触发go mod download,而非网络慢;需正确配置goproxy=https://goproxy.cn,direct、确保go.mod/go.sum已提交、挂载$gomodcache和$gocache目录,并避免go mod tidy修改文件导致缓存失效。

go build为什么总在重复下载模块
根本不是网络慢,而是每次执行 go build 时,工具链悄悄触发了 go mod download ——尤其当 go.mod 有未提交变更、或 GOPROXY 没设对时。国内直连 proxy.golang.org 会卡在 TLS 握手,单次超时默认 10 秒,且重试三次。
- 必须运行
go env -w GOPROXY=https://goproxy.cn,direct(direct是 fallback,保私有模块) - 确保
go.mod和go.sum已提交,CI 环境不能靠go mod tidy临时生成 - 避免在构建脚本里写
go mod tidy && go build——tidy会改go.mod,导致$GOCACHE失效 - CI 中挂载
$GOMODCACHE目录(通常是$HOME/go/pkg/mod),否则每次都是干净环境重下
模块缓存不生效的三个隐藏原因
$GOCACHE 和 $GOMODCACHE 是两套独立缓存,常被混为一谈。前者存编译产物(.a 文件),后者存下载的模块源码。两者都失效,构建就退化成“从零开始”。
-
$GOCACHE被静默禁用:检查go env GOCACHE,再用du -sh $(go env GOCACHE)看是否为空或磁盘满 -
CGO_ENABLED=0和CGO_ENABLED=1的构建结果不共享缓存 —— 同一项目别混用 - 用了
go run main.go调试?它默认绕过$GOCACHE,改用临时目录;要提速就写成go build -o /tmp/app && /tmp/app
大型项目只编译变更包的实操命令
90% 的日常开发只动了 cmd/ 或 internal/handler/ 下几个文件,没必要扫全量模块树。关键是让 go list 输出真实变更路径,再喂给 go build。
- 查修改的 Go 包:
git status --porcelain | grep '\.go$' | xargs dirname | sort -u | xargs go list -f '{{.ImportPath}}' - 过滤掉
vendor/和非主模块路径(加| grep '^myproject/') - 构建时显式传入:
go build -mod=readonly -trimpath $(上述命令) - CI 中并行构建多个 binary:
go build -o bin/a ./cmd/a && go build -o bin/b ./cmd/b,共享同一轮依赖解析
replace 和本地路径如何拖慢构建
replace 本身不慢,但配合 go list 扫描时会触发深度遍历 —— 尤其当 replace 指向一个含几十个子包的本地目录,go build 会递归检查所有子目录是否存在 go.mod,耗时飙升。
- 能用语义化版本 + Git Tag 就不用
replace;内部模块发布后,立刻go get myproj/internal@v0.3.1 - 必须用
replace时,目标路径尽量窄:replace example.com/a => ./a,而不是./ - 避免在
main.go里硬编码大量路由注册 —— 每增一个 handler,go list就得重扫整个包树 - 模板嵌入用
//go:embed时,路径别写**/*.html,改成明确路径如templates/*.html
go build 输出里,但 go build -x 一眼就能看到哪一步停了 8 秒。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











