Git Multi-Pack-Index (MIDX): Kỹ thuật gộp chỉ mục object tối ưu hiệu năng đọc cho repository quy mô lớn

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Khi repository trở thành nút cổ chai

Mình bắt đầu chú ý đến vấn đề này khoảng 8 tháng trước, khi team migrate một monorepo lớn — hơn 15 năm lịch sử commit, gần 2GB pack files — sang server mới. git status mất 3–4 giây, git log --oneline -10 cũng lag rõ. Lúc đầu nghĩ do phần cứng, nhưng sau khi profiling thì vấn đề nằm ở chỗ khác hoàn toàn: Git phải scan qua quá nhiều pack file riêng lẻ mỗi lần tìm object.

Git lưu object (commit, tree, blob) trong pack files — các file nén nhị phân trong .git/objects/pack/. Khi repository vừa được clone hoặc vừa chạy git gc, thường chỉ có 1–2 pack file nên mọi thứ nhanh. Nhưng sau nhiều tháng hoạt động, fetch liên tục, CI/CD chạy 24/7, số lượng pack file có thể lên đến vài chục. Mỗi pack file có index riêng (.idx), và Git phải binary search qua từng file một để tìm object — số lượng file càng nhiều, overhead càng lớn.

Giải pháp truyền thống là git gc --aggressive để gộp tất cả lại thành một pack duy nhất. Nhưng với repo 2GB, lệnh này chạy 20–30 phút và block toàn bộ operation trong thời gian đó. Không khả thi cho production.

Git Multi-Pack-Index giải quyết vấn đề như thế nào

Multi-Pack-Index (MIDX) — được giới thiệu từ Git 2.21, ổn định từ Git 2.25 — tạo ra một file index chung cho tất cả pack files hiện có, mà không cần gộp chúng lại. File này nằm tại .git/objects/pack/multi-pack-index.

Thay vì binary search tuần tự qua N file .idx, Git chỉ cần lookup một lần trong MIDX — rồi biết ngay object nằm ở pack file nào, offset bao nhiêu. Với 30 pack files, đây là cải thiện đáng kể mà không cần động đến dữ liệu gốc.

Điểm quan trọng nhất: MIDX không thay đổi pack files hiện có, không làm mất dữ liệu, không cần downtime. Tạo xong là dùng được ngay.

Kích hoạt MIDX

Git 2.25+ đã có MIDX built-in, không cần cài thêm gì. Kiểm tra version trước:

git --version
# git version 2.43.0 — OK, dùng được MIDX

Bật MIDX cho repository:

# Bật globally (khuyến nghị cho tất cả repo)
git config --global core.multiPackIndex true

# Hoặc chỉ cho repo hiện tại
git config core.multiPackIndex true

Tạo MIDX file lần đầu:

cd /path/to/your/repo
git multi-pack-index write

# Xác nhận file đã được tạo
ls -lh .git/objects/pack/multi-pack-index

Nếu repo lớn, lần đầu tạo MIDX có thể mất 10–30 giây — nhưng không block gì cả, Git vẫn phục vụ các operation khác bình thường trong lúc đó.

Cấu hình chi tiết và tích hợp workflow

Tự động cập nhật MIDX với git maintenance

MIDX tạo ra một lần rồi không tự update. Mỗi khi có pack file mới — sau fetch, sau gc — MIDX cũ không còn cover đủ nữa. Cần chạy lại git multi-pack-index write định kỳ.

Git 2.29+ có git maintenance xử lý chính xác việc này:

# Bật maintenance cho repo hiện tại
git maintenance start

# Kiểm tra dry-run trước
git maintenance run --task=incremental-repack --dry-run

git maintenance start thêm một cron job (Linux/macOS) hoặc Task Scheduler entry (Windows) chạy maintenance định kỳ. Task incremental-repack sẽ tự cập nhật MIDX sau mỗi fetch mà không cần can thiệp thủ công.

Nếu muốn kiểm soát thủ công hơn, thêm vào post-merge hook:

# .git/hooks/post-merge  (chmod +x sau khi tạo)
#!/bin/bash
git multi-pack-index write 2>/dev/null &

Chạy background (&) để không làm chậm merge/fetch operation của developer.

Kết hợp với commit-graph

Sau khi bật MIDX, mình cũng bật thêm commit-graph — hai tính năng bổ trợ nhau tốt. MIDX tối ưu object lookup, còn commit-graph tăng tốc reachability queries như git log --graph hay git merge-base:

git config --global fetch.writeCommitGraph true
git config --global core.multiPackIndex true

# Tạo cả hai cùng lúc
git multi-pack-index write --bitmap
git commit-graph write --reachable --changed-paths

Flag --bitmap khi write MIDX tạo thêm bitmap index — hữu ích cho server-side bare repository. Với local dev repo thì không cần thiết lắm.

Cấu hình cho bare repository trên server

Nếu bạn self-host Gitea hay GitLab, cấu hình server-side quan trọng hơn nhiều so với local:

# Trên bare repo (server)
git config pack.writeBitmaps true
git config repack.writeBitmaps true
git config core.multiPackIndex true

# Chạy maintenance — không block push/fetch của client
git maintenance run \
  --task=gc \
  --task=commit-graph \
  --task=incremental-repack

Schedule lệnh này chạy lúc 2 giờ sáng để tránh giờ cao điểm:

# crontab -e
0 2 * * * cd /path/to/bare.git && git maintenance run --task=incremental-repack --task=commit-graph 2>>/var/log/git-maintenance.log

Kiểm tra và Monitoring

Verify tính toàn vẹn

# Verify MIDX không bị lỗi (silent nếu OK, in lỗi nếu có vấn đề)
git multi-pack-index verify

# Xem thống kê chi tiết MIDX
git multi-pack-index verify --object-dir=.git/objects

# Debug xem Git có load MIDX không
GIT_TRACE2=1 git status 2>&1 | grep -i multi_pack

Đo cải thiện thực tế

Trước khi bật MIDX, mình benchmark bằng cách đơn giản:

# Xóa page cache OS trước mỗi lần đo (Linux)
sudo bash -c 'echo 3 > /proc/sys/vm/drop_caches'

# Đo thời gian KHÔNG có MIDX
time git rev-list --count HEAD
time git log --oneline -50 > /dev/null

# Tạo MIDX
git multi-pack-index write

# Drop cache rồi đo lại
sudo bash -c 'echo 3 > /proc/sys/vm/drop_caches'
time git rev-list --count HEAD
time git log --oneline -50 > /dev/null

Kết quả trên repo của team (28 pack files, ~1.8GB): git rev-list --count HEAD từ 4.2s xuống còn 0.8s. git log --oneline -50 từ 2.1s xuống 0.4s. Trong team 8 người, mình áp dụng Git config này cho tất cả developer machine và server CI/CD — số lần phàn nàn về “git chậm” giảm hẳn, đặc biệt sau các sprint merge nhiều branch dài.

Khi nào cần rebuild MIDX

Một vài tình huống cần chạy lại git multi-pack-index write:

  • Sau mỗi git fetch tạo pack file mới (nếu không dùng maintenance hook)
  • Sau git gc hoặc git repack — các lệnh này thay đổi pack files
  • Sau rebase lớn hoặc merge branch dài với nhiều commit
  • Khi số lượng loose objects tăng đột biến
# Kiểm tra số pack files hiện tại
ls .git/objects/pack/*.pack | wc -l

# Nếu > 10, nên write lại MIDX
git multi-pack-index write

Nếu số lượng pack files vượt 50–100, nên cân nhắc chạy git repack -adf một lần để gộp lại — MIDX giúp trì hoãn thời điểm cần làm việc đó, không tránh hoàn toàn mãi mãi.

Một số lưu ý thực tế sau 6 tháng dùng production

  • Git 2.25 trở lên là đủ — Ubuntu 20.04 LTS đi kèm Git 2.25.1, đủ để dùng MIDX cơ bản. Muốn dùng git maintenance thì cần Git 2.29+.
  • MIDX chỉ tối ưu read operation — git add, git commit, git push không nhanh hơn. Hiệu quả rõ nhất với: git log, git status, git fetch, git merge.
  • File MIDX nhỏ bất ngờ — repo 1.8GB pack files nhưng file multi-pack-index chỉ ~2MB. Không tốn dung lượng đáng kể.
  • Safe to delete và rollback — xóa file MIDX chỉ làm Git chậm lại, không mất dữ liệu. Có thể test và rollback thoải mái bất cứ lúc nào.
  • Windows cũng được — Git for Windows 2.25+ hỗ trợ đầy đủ, không cần cấu hình thêm gì.

Đây không phải silver bullet, nhưng là một tweak đáng bật ngay nếu repo của bạn đã lớn hơn vài trăm MB và bắt đầu có cảm giác lag trên các lệnh đọc thông thường.

Share: