go build ./...在多模块项目中更慢,根本原因是强制扫描整个模块树,触发大量go list调用和模块校验,尤其含replace或本地路径时单次卡顿可达数秒;应改用git+go list精准定位变更包再构建。

为什么go build ./...在多模块项目里反而更慢
根本不是 Go 编译器变慢,而是 go build ./... 会强制扫描整个模块树——哪怕你只改了 api/ 下一个文件,它仍要递归遍历所有 internal/、pkg/、vendor/ 和 replace 路径下的包,触发大量 go list 调用和模块校验。尤其当 go.mod 里有本地 replace 或跨仓库依赖时,每次都要重新解析路径、校验 checksum,单次卡顿可达数秒。
只编译真正变更的模块:用git + go list精准定位
日常开发中,90% 的修改只影响少数几个子模块。与其全量扫,不如让构建“知道”哪些包变了:
- 查出本次修改涉及的 Go 文件所在目录:
git status --porcelain | grep '\.go$' | xargs dirname | sort -u - 转成 import path:
git status --porcelain | grep '\.go$' | xargs dirname | sort -u | xargs go list -f '{{.ImportPath}}' - 只构建这些路径及其直接依赖:
go build $(上述命令)
注意:go list 输出可能含 vendor/ 前缀,若项目未启用 -mod=vendor,需加 | grep -v '^vendor/' 过滤,否则会漏掉主模块外的真实变更。
并行构建多个 binary 时缓存不共享?检查 CGO_ENABLED 和 GOOS/GOARCH
同一项目里 go build -o bin/a ./cmd/a 和 go build -o bin/b ./cmd/b 看似能并行,但若其中一个启用了 CGO_ENABLED=1(比如调用了 SQLite),另一个是 CGO_ENABLED=0,它们的 $GOCACHE 子目录完全隔离,无法复用标准库和依赖的编译产物。同理,混用 GOOS=linux 和 GOOS=darwin 也会分隔缓存。
实操建议:
- CI 中统一设置
CGO_ENABLED=0(除非真需 cgo),避免缓存分裂 - 交叉编译前先清理无关缓存:
go clean -cache -modcache,再用GOOS=xxx go build - 用
go env GOCACHE查路径,再du -sh $(go env GOCACHE)/*看各子目录大小,确认是否被异常占用
go run 为什么总比 go build 慢得多
go run 默认绕过 $GOCACHE,每次都在临时目录生成中间文件、链接、执行、再删除——尤其在 Windows 上,反病毒软件对临时文件高频扫描,叠加 NTFS 小文件 I/O 延迟,go run main.go 可能比 go build -o a && ./a 慢 3–5 倍。
如果你非要热重载,别直接 go run:
- 改用
go build -mod=readonly -trimpath -o /tmp/app && /tmp/app,强制走缓存 - 搭配
air时,在.air.toml中禁用build_delay和自动生成逻辑,只让它监听.go文件并执行你写好的构建命令 - Windows 用户务必关掉
PCManager Service Store类服务,它们会劫持进程创建和临时文件操作
真正卡住的地方,往往不是“要不要并行”,而是构建命令是否精准命中变更范围、缓存路径是否被不同构建模式撕裂、以及 go run 这类便利命令背后隐藏的 I/O 代价——这些细节不处理,加再多核也白搭。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











