目录

Gerrit 大型仓库加速与索引维护实战指南

https://gitenterprise.me/wp-content/uploads/2024/12/gerrit-3.10-3.11.png

目标:在 Gerrit(裸仓库)环境中,通过对 Git pack、bitmap 索引、commit-graph 与 Gerrit/Lucene 索引的系统化维护,显著缩短 git clone / fetch 时的 “Finding sources” 与其它慢步骤,提升整体 CI/CD 与开发效率。


先决条件(环境概览)

角色 设备 / 软件 规格 / 说明
服务端(Gerrit) 裸金属:FusionServer 2288H V6 双路 Intel Xeon Gold 6330 ×2(每路 28 cores, 56 threads),内存 125GB,/data:15TB NVMe SSD;Gerrit v3.10.2
客户端(构建 / CI) 深信服超融合 (SANGFOR HCI) VM → Docker 容器 Ubuntu 22.04 LTS 容器,64 cores / 128 threads,内存 128GB,/data:15TB NVMe SSD
网络 同一局域网 低延迟局域网(但仍可能受服务器 I/O/CPU 影响)

问题描述(现象)

在客户端执行:

git clone "ssh://[email protected]:29418/Chery/T29-T16A/.../co_common" -b baseline-mp1

出现极慢的克隆过程:

Cloning into 'co_common'...
remote: Counting objects: 9907586, done
remote: Finding sources:   4% (361750/9907586)

观测:Finding sources 阶段非常慢(每秒仅加载 ~1500 个对象),导致整体 clone 时长大幅延长。


快速结论(概览)

  1. 根因:服务器端缺少或未及时更新 bitmap indexcommit-graph,导致在协商对象集合(finding sources)时退化为对提交 DAG 的全量遍历。
  2. 次要影响:若仓库未做紧凑 repack,Pack 分散/过多,服务器在生成/复用 pack 时也会耗费 CPU/IO,进一步延长 Compressing objects 阶段。
  3. 解决方向:定期对仓库执行 git repack -adf --write-bitmap-indexgit commit-graph write --reachable --changed-paths,并在 Gerrit 层面重建 Lucene 索引(必要时)。

详细成因分析

1. Counting objects 阶段

  • 读取对象索引(pack / .idx)并统计元数据;此阶段通常较快,受磁盘 I/O 与读取索引性能影响。

2. Finding sources 阶段(核心瓶颈)

  • 目的:服务器判断客户机已有对象集合与目标 refs 的差集(即要发送哪些对象)。
  • bitmap indexcommit-graph 可用时,服务器使用位运算与已索引的可达性信息快速计算差集;否则会走提交 DAG 遍历(深度优先/广度优先),这是在超大仓库上非常耗时的操作。

3. Compressing objects / sending pack 阶段

  • 一旦需要发送对象集合,服务器会尝试复用现有 pack 或实时打包(pack-objects)。如果仓库没有紧凑 pack,实时打包会占用大量 CPU/IO;NVMe 和大内存能缓解,但无助于前一步骤耗时。

运维与优化(逐项、可执行命令)

在裸仓库(Gerrit 的 .git 即裸仓库)中运行以下维护命令。请先在维护窗口执行,并做好短期备份。

A. 对单仓库的维护(推荐先执行在示例仓库)

# 进入裸仓库目录(示例)
cd /data/sdc/cockpit-gerrit-8082/git/Chery/T29-T16A/MediaTek_MTK8678/yocto/BITECH/mt8678-yocto/src/kernel/linux/v6.6_mt8678/co_common.git

# 备份现有 pack 目录(短期)
cp -r objects/pack ../pack_backup_$(date +%F)

# 强制 repack:合并 pack、产生新的紧凑 pack,并写入 bitmap index
git repack -adf --write-bitmap-index --threads=16

# 生成 commit-graph,记录 changed-paths(提升按路径查询效率)
git commit-graph write --reachable --changed-paths

# 验证 bitmap 与 commit-graph 文件存在
ls -lh objects/pack/*.bitmap || true
ls -lh objects/info/commit-graph* || true

说明

  • -a:将所有 loose objects 也打包。
  • -d:删除旧的 pack(视操作风险,可先不加以保留备份)。
  • -f:强制生成(覆盖可能存在的旧索引)。
  • --write-bitmap-index:为 pack 生成 .bitmap 文件。

B. Gerrit 层面的 Lucene 索引(用于代码搜索 / UI 查询加速)

在 Gerrit 服务器上,通过 gerrit 管理命令触发索引重建:

# 重新为所有项目重建索引(消耗资源,建议低峰期执行)
ssh -p 29418 gerrit@localhost gerrit index start --all

# 或者单项目重建
ssh -p 29418 gerrit@localhost gerrit index start --project "Chery/T29-T16A/.../co_common"

注意:Lucene 索引主要影响 Gerrit UI 的查询与搜索性能,而非 git 协议本身的 clone/fetch 逻辑;但在一些管理操作或插件中,过慢的索引会间接影响用户体验。


C. 批量 / 自动化维护脚本(示例)

定期对仓库做轮询维护:

#!/bin/bash
set -euo pipefail
GERRIT_SITE="/data/sdc/cockpit-gerrit-8082/git"
LOGFILE="/var/log/gerrit_maint_$(date +%F).log"
THREADS=12

cd "$GERRIT_SITE"

for repo in $(find . -name "*.git" -type d); do
  echo "$(date +%F_%T) - Optimizing $repo" | tee -a "$LOGFILE"
  cd "$repo" || continue
  # 先备份 pack(可选,按需开启)
  # cp -r objects/pack ../pack_backup_$(date +%F)

  # repack(避免并发太多,按仓库顺序进行)
  git repack -adf --write-bitmap-index --threads=${THREADS}

  # commit-graph
  git commit-graph write --reachable --changed-paths || true

  cd "$GERRIT_SITE"
  # sleep 2 # 可选,避免短时间内连续占满 I/O
done

# 可选触发 Gerrit 索引(谨慎)
# ssh -p 29418 gerrit@localhost gerrit index start --all

建议:通过 systemd timer 或 cron 在低峰时间(如周末夜间)触发。脚本中应加入日志、错误处理与报警机制。


客户端 / 网络层优化建议

  • 在客户端 .gitconfig 配置并行解压:
[pack]
    threads = 8
  • 若仅需某个分支或目录,使用 浅克隆 / 单分支 限制传输数据量:
# 单分支
git clone --single-branch -b baseline-mp1 <repo-url>

# 浅克隆最近 N 次提交(若不需要全部历史)
git clone --depth 1 --branch baseline-mp1 <repo-url>
  • 在局域网环境下,如果仍慢,排查以下指标:

    • 服务器端 CPU、iowait、磁盘队列(top / iostat / iotop
    • 网络丢包、MTU 设置
    • Gerrit 进程是否在同时执行重打包或 GC 操作

概念速查(技术解释)

Pack / Pack Index (.pack / .idx)

  • 作用:将大量 loose objects 合并并压缩以减少磁盘与网络带宽开销。
  • 位置objects/pack/pack-<hash>.pack 与对应 .idx
  • 运维要点:保持较少且紧凑的 pack 通常高效;不宜频繁在高峰期执行 repack

Bitmap Index

  • 作用:用位图表示从某个 ref 可达的对象集合,支持位运算快速计算差集。
  • 优势:显著加速 finding sources 阶段,特别是超大仓库。
  • 生成方式:在 repack 时加 --write-bitmap-index

Commit-Graph

  • 作用:预计算 commit 的可达性与 parent 元信息,加速 git rev-list、可达性检查与按路径历史查询(配合 --changed-paths)。
  • 生成方式git commit-graph write --reachable --changed-paths

Compressing objects / sending pack

  • 流程:服务器根据需要的对象集打包(可能复用已有 pack),对对象做 delta 压缩并通过网络发送。
  • 瓶颈:如果没有预先紧凑的 pack 或服务器需要实时创建 pack,会占用大量 CPU/IO。

验证与故障排查清单

  1. 验证生成的索引

    • ls objects/pack/*.bitmap — 应存在 .bitmap 文件。
    • ls objects/info/commit-graph* — 应存在 commit-graph 文件。
  2. 在客户端复测

    • 在另一个机器上进行 git clone --single-branch -b baseline-mp1 <url>,对比速度。
  3. 监控指标

    • tophtop:查看 CPU/线程占用。
    • iostat -x 1iotop:磁盘 I/O 与延迟。
    • Gerrit 日志(error_log / gerrit.log):观察索引/repacks 相关报错。
  4. 回滚策略:若 repack 后出现问题,可从 pack_backup_YYYY-MM-DD 恢复旧的 pack 目录(谨慎操作,并保证服务短暂停止或在维护窗口恢复)。


维护频率建议

  • 热仓库(每日大量 push)

    • commit-graph:每天或每次大批量 push 后更新。
    • repack + bitmap:每周或每次发布/版本切割后执行。
  • 中等活跃仓库:每月一次 repack + bitmapcommit-graph 每周或每日一次视情况。

  • 小型/冷门仓库:每季度或按需。


风险与注意事项

  • git repack -adf --threadsgit commit-graph write 都是 CPU/内存/IO 密集型 操作,务必在低峰窗口执行。
  • 在运行 -d 删除旧 pack 前,保留短期备份,以便回滚。
  • 若 Gerrit 自身对仓库有并发写入(push),应考虑暂停写入或协调维护时段。

附:常用命令速查表

# 重新打包并生成 bitmap
git repack -adf --write-bitmap-index --threads=16

# 生成 commit-graph(记录 changed-paths)
git commit-graph write --reachable --changed-paths

# 验证 bitmap
ls objects/pack/*.bitmap

# 触发 Gerrit 全量索引重建(谨慎)
ssh -p 29418 gerrit@localhost gerrit index start --all

后续建议(实践落地)

  1. 单个最慢仓库 上先做一次完整维护(repack + commit-graph),并记录 git clone 前后时间对比。
  2. 制定分批维护计划(避免同时针对所有仓库并发 repack)。
  3. 将维护脚本集成到 systemd timer 或 cron,并加入邮件/Telegram 报警(若日志出现错误)。
  4. 按需优化 Gerrit 的 JVM / Lucene 配置,确保索引重建时有足够内存。