极狐GitLab大仓库性能优化,克隆加速与仓库瘦身实战
极狐GitLab大仓库性能优化,克隆加速与仓库瘦身实战
大仓库性能优化:通过部分克隆、大文件外置、服务端缓存与历史重写等手段,降低超大型 Git 代码仓库克隆耗时与存储占用的系统性工程实践。
一个跑了两三年的 monorepo,克隆动辄半小时起步,新同事入职第一天就卡在 git clone 上——这不是玄学,是历史里的二进制文件、碎片化的 packfile 和没人管的松散对象在同步计息。好消息是,极狐GitLab 在客户端和服务端都给了现成的解法,本文按「先止血、再根治」的顺序讲清楚。
先看清楚,仓库到底大在哪
在动手之前,先明确官方定义:代码仓库大小是仓库中所有文件的累计大小,计算方式类似在仓库哈希存储路径上执行 du --summarize --bytes,且仓库变化时每 15 分钟最多重新计算一次(见代码仓库大小官方文档)。
有一个常见的认知误区必须先纠正:在新提交里删除文件,不会立即减小仓库大小。因为较早的提交和 blob 仍然存在于历史中。要真正瘦身,要么把大文件挪出去,要么重写历史。这正是下面两条路线的分野。
克隆加速:客户端与服务端各管一摊
克隆慢的治理要分清责任边界。客户端优化解决「下多少」的问题,服务端优化解决「给多快」的问题,两类手段对比:
手段 | 作用层 | 典型场景 | 效果边界 |
|---|---|---|---|
部分克隆(Partial Clone) | 客户端 | monorepo 只用局部代码 | 只拉取所需对象,按需补齐 |
Git LFS | 仓库结构 | 二进制大文件频繁变更 | 大文件出仓库,仓库只剩指针 |
GIT_DEPTH 浅克隆 | CI/CD | 流水线构建 | 只取最近 N 个提交 |
pack-objects 缓存 | 服务端 Gitaly | CI 高频重复拉取 | 相同请求直接回缓存 |
部分克隆是 Git 原生能力,核心是过滤器语法:blob:none 不拉取文件内容、blob:limit= 跳过小于阈值的 blob、tree: 按目录深度过滤。检出时 Git 再按需向远端补齐对象,详见部分克隆官方文档。
Git LFS 则从仓库结构上根治二进制问题:它是开源 Git 扩展,将大型二进制文件存入对象存储,仓库里只留一个含 SHA256 哈希与文件大小的文本指针。二进制不再进历史,clone 与 fetch 自然不再被反复变更大文件拖垮,参考Git LFS 官方文档。落地上只需两步:
# 1. 初始化 LFS(自动添加 pre-push 钩子与 .gitattributes)
git lfs install
# 2. 声明需要 LFS 管理的文件类型
git lfs track "*.psd" "*.mp4"服务端的 pack-objects 缓存值得单独展开。CI 场景下成百上千个 Runner 拉的是几乎相同的 packfile,Gitaly 的 pack-objects 缓存可以把相同请求的结果直接回放,省掉重复的 git pack-objects 计算。在 /etc/gitlab/gitlab.rb 中开启:
gitaly['configuration'] = {
pack_objects_cache: {
enabled: true,
# dir / max_age / min_occurrences 默认值即可满足多数场景
},
}按官方文档,该缓存默认关闭(可能带来可观的磁盘写入 IO 增量),min_occurrences 建议保持为 1——JihuLab.com 生产环境将其从 0 调到 1 后节省了 50% 的缓存磁盘空间,而命中率几乎不受影响。可通过 Prometheus 指标 gitaly_pack_objects_cache_lookups_total(按 result="hit"/"miss" 打标)观察命中率,配置细节见配置 Gitaly 官方文档。
要诚实说明的边界:这个缓存只对重复的相同 fetch 有效。某人偶尔克隆一次仓库做代码走查,属于唯一请求,缓存帮不上忙。
仓库瘦身:四步重写,一步都别省
如果历史里已经堆满了大文件,缓存和 LFS 都救不了存量,只能走历史重写。官方给出的可落地流程如下:
找出大文件:用
git ls-tree -r -t --long | sort -nk 4列出按大小排序的 blob,第三列即对象 ID。重写历史:使用
git filter-repo从整个 Git 历史中清除大文件。注意此操作修改全部历史、不可逆、需全员重新克隆,且不能用于删除敏感数据。上传清理清单:进入「设置 > 代码仓库」,展开代码仓库维护,上传 filter-repo 生成的
commit-map文件启动清理。文件过大时用split -l 20000 filter-repo/commit-map filter-repo/commit-map-拆分上传,避免后台清理超时。例行维护收尾:在「设置 > 通用 > 高级」选择运行例行维护,等待至少 30 分钟后,再选择清理不可达对象。这是因为 Git 垃圾回收带有 30 分钟宽限期,太新的不可达对象不会被删除。
三种瘦身方法的能力边界差异很大,选错方法会白干:
方法 | 适用目的 | 是否重写历史 | 关键限制 |
|---|---|---|---|
从历史清除文件 | 删除大文件 | 是 | 不影响 LFS 对象,需全员重新克隆 |
清理代码仓库 | 移除内部引用与未引用对象 | 否(运行 | 不能删除敏感数据 |
移除 blob | 永久删除敏感信息 | 是 | 需所有者角色,项目不能是 fork |
例行维护本身也值得配置到位。极狐GitLab 支持主动与启发式两种例行维护策略,启发式策略会根据松散对象数量、packfile 数量、松散引用和 commit-graph 是否存在来决定优化动作,避免大仓库被过于频繁的全量重打包拖慢;私有化部署管理员还可在 Gitaly 中配置每日后台维护窗口,例如每天 23:00 开始持续 1 小时(daily_maintenance 配置),详见例行维护官方文档。
FAQ
Q1:删掉文件再提交,仓库大小为什么没变小?
因为历史提交中的旧 blob 依然存在,仓库大小统计的是全部 Git 对象。新提交中删除文件只是不再检出它,要真正减掉体积必须用 git filter-repo 重写历史,再配合例行维护与清理不可达对象。
Q2:pack-objects 缓存能加速所有人的克隆吗?
不能。它只加速内容相同的重复 fetch,典型受益者是 CI 场景下高频拉取的 Runner。人类开发者的首次克隆属于唯一请求,不会有缓存命中;且该功能默认关闭,开启前需评估磁盘写入 IO 增量。
Q3:git filter-repo 可以顺便删除泄露的密钥吗?
官方不建议。重写历史可能遗漏 fork、缓存数据库中的提交信息。要删除敏感数据应使用「移除 blob」或「遮盖文本」功能,同时立即撤销或轮换已泄露的密钥,并开启密钥检测防止再次泄露。
写在最后
大仓库优化没有银弹,但有一条清晰的次序:先用 LFS 止住二进制增量,再用部分克隆和浅克隆改善日常体验,服务端开 pack-objects 缓存喂饱 CI,最后对存量历史做一次干净的重写。每一步在极狐GitLab 里都有官方文档背书与现成配置,不需要魔改基础设施。
如果你的团队正被几十 GB 的仓库折磨,建议先做一次存储画像再对症下药。可预约极狐GitLab 演示,或前往官方文档中心查阅仓库维护与存储管理的完整章节,免费试用入口同站可得。

