13 điểm bởi GN⁺ 2025-08-16 | 1 bình luận | Chia sẻ qua WhatsApp
  • Dự án Git gần đây đã chính thức bắt đầu trực tiếp giải quyết vấn đề quản lý tệp dung lượng lớn
  • Git LFS là một giải pháp tình thế, gây ra nhiều chi phí và sự phụ thuộc vào nhà cung cấp cho người dùng
  • Gần đây, với tính năng partial clone, chỉ riêng Git cũng đã có thể thay thế phần lớn vai trò của LFS
  • Trong tương lai, một giải pháp mới tên là large object promisor cũng đang được chuẩn bị để tích hợp vào Git chính thức
  • Những thay đổi này cho thấy lời giải cuối cùng cho việc quản lý tệp dung lượng lớn sẽ quy về chính Git, thay vì các phần mở rộng bên ngoài

Vấn đề tệp dung lượng lớn của Git và những thay đổi đang diễn ra

  • Nếu có một "khắc tinh" lớn nhất của Git, thì đó chính là tệp dung lượng lớn
  • Tệp dung lượng lớn làm phình to kho lưu trữ Git, làm chậm git clone, và gây ảnh hưởng xấu tới hầu hết các môi trường hosting

Sự xuất hiện và giới hạn của Git LFS

  • Năm 2015, GitHub đã phát hành Git LFS để lách qua vấn đề tệp dung lượng lớn
  • Tuy nhiên, bản thân Git LFS lại làm phát sinh thêm độ phức tạp và chi phí lưu trữ
  • Cộng đồng Git từ lâu đã âm thầm suy nghĩ về bài toán cốt lõi của tệp dung lượng lớn, và trong các bản phát hành chính thức gần đây của Git, một hướng đi mới để quản lý tệp dung lượng lớn mà không cần LFS đã được đưa ra

Cách có thể áp dụng ngay hôm nay: thay Git LFS bằng partial clone

  • Nguyên lý của partial clone

    • Git LFS: tệp dung lượng lớn được đặt bên ngoài kho lưu trữ, và chỉ tải về những tệp cần thiết để làm việc
    • Git partial clone (được giới thiệu năm 2017):
      • Dùng tùy chọn --filter để sao chép mà loại trừ các blob lớn hơn kích thước mong muốn
      • Chỉ khi cần mới tải chính tệp dung lượng lớn đó từ máy chủ

      Khi dùng partial clone, bạn không cần tải trước [các tài sản nhị phân dung lượng lớn] trong lúc Clone và Fetch, nhờ đó giảm thời gian tải xuống và dung lượng đĩa sử dụng

      Quảng cáo
  • Điểm chung giữa partial clone và LFS

    • 1. Giảm tối thiểu dung lượng checkout: chỉ lấy phiên bản mới nhất và bỏ qua toàn bộ lịch sử tệp
    • 2. Sao chép nhanh: không phải truyền tệp dung lượng lớn nên tốc độ clone nhanh hơn
    • 3. Thiết lập nhanh: khác với shallow clone, vẫn có thể truy cập toàn bộ lịch sử dự án
  • Ví dụ sử dụng partial clone

    • Ví dụ thực tế về tốc độ sao chép repo và dung lượng đĩa khi một repo có nhiều lịch sử của các tệp PNG lớn:
      • clone thông thường mất gần 4 phút, chiếm 1.3GB
      • với partial clone và giới hạn blob 100KB, chỉ mất 6 giây để clone, chiếm 49MB
      • cải thiện 97% tốc độ sao chép và giảm 96% kích thước checkout so với bản gốc
      Quảng cáo
  • Giới hạn của partial clone

    • Khi cần đến dữ liệu đã bị lọc (ví dụ: git diff, git blame, git checkout), Git sẽ yêu cầu tệp từ máy chủ
    • Đây là đặc tính giống hệt Git LFS
    • Trong công việc thực tế, hiếm khi cần chạy blame trên tệp nhị phân

Các vấn đề của Git LFS

  • Mức độ phụ thuộc nhà cung cấp cao: bản triển khai của GitHub chỉ hỗ trợ máy chủ riêng của họ, kéo theo phí và sự phụ thuộc
  • Vấn đề chi phí: lưu 50GB trên GitHub LFS tốn $40/năm, còn Amazon S3 là $13
  • Khó quay lại: một khi đã chuyển sang LFS thì không thể khôi phục nguyên trạng nếu không viết lại lịch sử
  • Chi phí thiết lập liên tục: mọi cộng tác viên đều phải cài LFS; nếu không cài thì thay vì tệp thật sẽ nhận được tệp metadata, dễ gây nhầm lẫn

Triển vọng sắp tới: Large Object Promisor

  • Tệp dung lượng lớn cũng gây ra vấn đề chi phí cho các nền tảng hosting như GitHub và GitLab
  • Git LFS giảm chi phí máy chủ bằng cách offload tệp dung lượng lớn sang CDN
  • Large Object Promisor là gì?

    • Đầu năm nay, Git đã chính thức merge một tính năng tên là large object promisor
    • Tính năng này giảm tải lưu trữ phía máy chủ tương tự LFS, nhưng cắt giảm đáng kể độ phức tạp phía người dùng

      Nỗ lực này nhằm cải thiện công việc ở phía máy chủ, đặc biệt là với các blob lớn đã được nén ở dạng nhị phân
      Đây là một giải pháp thay thế cho Git LFS
      Large Object Promisors, git-scm.com

      Quảng cáo
  • Cơ chế hoạt động

    • 1. Người dùng push tệp dung lượng lớn lên Git host
    • 2. Host xử lý offload các tệp dung lượng lớn sang promisor ở backend
    • 3. Khi clone, Git host cung cấp thông tin promisor cho client
    • 4. Khi cần, client sẽ tự động nhận tệp dung lượng lớn từ promisor đó
  • Tình hình triển khai hiện tại và các thách thức

    • Large object promisor vẫn đang trong quá trình phát triển, và đến tháng 3/2025 một phần mã đã được merge vào Git
    • Các triển khai bổ sung và những vấn đề chưa được giải quyết đang tiếp tục được thảo luận tại GitLab và những nơi khác
    • Vẫn cần thêm thời gian trước khi được áp dụng rộng rãi
    • Trong thời gian trước mắt, việc lưu trữ tệp dung lượng lớn vẫn khó tránh khỏi phụ thuộc vào Git LFS
    • Khi promisor được phổ biến, GitHub cũng được kỳ vọng sẽ cho phép tải lên các tệp vượt quá 100MB

Kết luận: Tương lai tệp dung lượng lớn của Git là Git

  • Dự án Git đang không ngừng suy nghĩ về vấn đề tệp dung lượng lớn thay cho bạn
  • Hiện tại, việc dùng Git LFS vẫn còn cần thiết
  • Tuy nhiên, khi partial clone và large object promisors tiếp tục phát triển, Git LFS sẽ dần trở nên không còn cần thiết, và chẳng bao lâu nữa chúng ta có thể dễ dàng quản lý tệp dung lượng lớn chỉ bằng Git
  • Trong tương lai, rào cản cuối cùng đối với việc dùng tệp dung lượng lớn có lẽ chỉ còn là ý nghĩ muốn đưa cả thư viện MP3 vào Git

1 bình luận

 
GN⁺ 2025-08-16
Ý kiến trên Hacker News
  • Ngay cả thời svn trước đây cũng hoạt động khá ổn với thư mục làm việc 150GB chứa nhiều file nhị phân lớn, trong khi git thì không. Tôi thắc mắc vì sao svn lại có cách tiếp cận khác git với các file nhị phân lớn, và liệu git có thể làm giống vậy không. Ngoài ra, tính năng cực kỳ cần thiết khi xử lý dữ liệu nhị phân là khóa file, để chỉ một người có thể chỉnh sửa một file cụ thể còn những người khác chỉ đọc, nhằm tránh các vấn đề merge rối rắm. Tôi cũng không thật sự hiểu việc offload các file lớn ra bên ngoài có cải thiện được vấn đề hiệu năng hay độ ổn định trong thực tế không. Rốt cuộc chỉ là kho lưu trữ khác mà thôi, vẫn là kho lưu trữ. Việc offload này có vẻ thực chất là cách để các public git forge né chi phí lưu trữ file lớn

    • git và svn có thiết kế hoàn toàn khác nhau. git là hệ thống phân tán hoàn toàn nên mọi kho đều phải có toàn bộ lịch sử của mọi file, và khái niệm khóa hầu như vô nghĩa. Chỉ cần chọn hệ thống quản lý phiên bản phù hợp với dự án, không phải git lúc nào cũng phải là lời giải cho mọi thứ
  • Tôi thích khái niệm large object promisors. Nếu có thể dễ dàng nối với thứ như S3 thì tôi có lẽ sẽ chuyển ngay từ LFS hiện tại. S3 có tính cộng hưởng rất tốt khi quản lý phiên bản cho các file nhị phân lớn. Intelligent Tiering cũng có lợi thế là dữ liệu càng cũ thì tự động được chuyển sang tầng lưu trữ rẻ hơn. Có phải mất nửa ngày để khôi phục dữ liệu 10 năm tuổi thì tôi cũng không bận tâm

    • Tôi cũng nghĩ vậy. Tôi không hiểu sao ngay từ đầu đây lại không phải là cách mặc định. Tôi đang tự chạy một git LFS server nhỏ, và chỉ cần git hỗ trợ S3 native là tôi sẵn sàng chuyển ngay

    • Ở chỗ làm hiện tại, chúng tôi đang cache các LFS object trong bucket để cắt giảm chi phí. Mỗi lần chạy PR, chúng tôi lấy danh sách file bằng git lfs ls-files, tải từ gcp, lưu object vào local bằng git lfs checkout, rồi pull để chỉ nhận thêm những gì còn thiếu. Các file chưa được cache thì lại được đẩy lên bucket bằng gcloud storage rsync. Từ góc nhìn của developer, không cần cấu hình thêm gì, chỉ cần pull object mới, và UI của GitHub cũng không bị rối về trạng thái repo. Trước đây chúng tôi từng nghĩ tới việc tự dựng backend LFS, nhưng cách này giải quyết được vấn đề lớn nhất trước mắt. GitHub tính phí băng thông quá cao mỗi lần CI tải file LFS, và vì giới hạn cache 10GB cùng việc không chia sẻ được giữa các branch nên lần nào cũng phải tải lại. Tôi thậm chí muốn trả tiền để tăng dung lượng cache mà cũng không có cách. Nếu muốn áp dụng cho developer thì chỉ cần thêm một git hook là đủ đơn giản

    • S3 có phải là dịch vụ liên quan tới Amazon không?

  • Bài viết nêu ra nhiều điểm Git LFS chưa tốt, nhưng tôi không đồng ý với ý cho rằng nó gây vendor lock-in. GitHub cung cấp cả client và server mã nguồn mở nên lập luận đó không thỏa đáng. Dù vậy, LFS không hoạt động trong các tình huống offline/sneakernet, đây không phải trường hợp phổ biến nhưng vẫn đáng được nhắc tới. Large object promisor có vẻ như chuyển sự phức tạp phía client của LFS sang phía server, nên cuối cùng chỉ là dời chỗ phức tạp. Nếu cấu trúc là git server tải file lên LFS server và object storage, thì sẽ có các trade-off khác. Tôi tò mò điều gì sẽ xảy ra khi public git server giấu promisor remote đi và xử lý lúc upload

    • Tôi thật sự thấy LFS khá tệ. Cách triển khai server cũng lộn xộn, và nội dung object bị trộn lẫn với cách lưu trữ. Cách opt-in cũng rất dở, nên nếu dùng mà không để ý thì bạn chỉ nhận được các file text nhỏ thay vì file thật sự mình muốn. Tôi không chắc giải pháp mới có tốt hơn không, nhưng việc LFS không tốt thì là điều khá rõ

    • Một vấn đề khác của Git LFS mà gần đây tôi mới biết là migration làm ô nhiễm cả .gitattributes ở các commit phía trên. Tức là nếu chuỗi commit là A→B→C và chỉ ở C mới thêm file lớn vào LFS, thì A và B cũng sẽ xuất hiện .gitattributes trỏ tới các file LFS không tồn tại. Trong quá trình migration, .gitattributes bị lan ngược theo lịch sử vì nó không kiểm tra xem thực thể đó có tồn tại ở commit hiện tại hay không

    • Trước đây Git LFS không hỗ trợ SSH, nên bắt buộc phải có chứng chỉ SSL, điều này là rào cản với những ai tự host ở nhà. Có vẻ GitLab gần đây đã vá để hỗ trợ SSH

  • Trong lớp kỹ thuật phần mềm, tôi từng được khuyên không đưa các file lớn như media vào Git mà thay vào đó đưa vào artifact repository như Artifactory. Khi đó có thể phân phối theo dạng snapshot dependency, và build system sẽ tự kiểm soát để chỉ lấy phiên bản mới nhất. Các file cũ tích tụ ở local của đồng nghiệp cũng có thể dọn ngay chỉ bằng cách xóa cache của build system

    • Cách này hơi mang cảm giác giống git submodule. Nếu submodule giải quyết được vấn đề thì chắc người ta đã dùng rồi. git submodule cũng hỗ trợ shallow clone (liên kết liên quan: https://stackoverflow.com/questions/2144406/how-to-make-shallow-git-submodules). Tôi chưa từng gặp bài toán file lớn nên khá tò mò vì sao cách này lại không dùng được. Các nhược điểm trên SO trông không đến mức quá lớn

    • Tôi tò mò không biết trong lớp có dạy cả kiến trúc hệ thống CI/CD không. Kỹ sư mới ra trường dạo này thường không quen với toàn bộ cấu trúc tích hợp GitLab, Artifactory, CodeSonar, Anchore, v.v.

    • Cách này cũng có nhược điểm riêng. CI/CD system hoặc developer sẽ cần thêm thông tin xác thực. Commit cũng tăng thành nhiều bước hơn, và phải biết artifact ID trước. Nếu cố tự động hóa bằng git hook thì cuối cùng lại phức tạp kiểu git-lfs

  • Bài này đang đánh giá LFS không công bằng. LFS không phụ thuộc GitHub và giao thức cũng là mở. Nhược điểm của LFS là phần nào không thể tránh khỏi khi nó là một phần mở rộng của git, và promisors thực chất cũng gần như cùng một ý tưởng với LFS. Chỉ là khi được tích hợp vào bên trong git thì UX tốt hơn một chút

    • Một khi repo đã từng dùng LFS thì sẽ bị lock-in vĩnh viễn. Muốn giảm dung lượng đã sử dụng thì phải xóa hẳn repo, và điều này cũng không được thông báo rõ ràng một cách chính thức. Khi nghiên cứu thống kê GitHub của công ty, chúng tôi từng đưa file DB nén lớn vào LFS và đã tự mình gặp đúng vấn đề này
  • Đây không phải giải pháp thật sự. git LFS cũng chỉ là giải pháp tạm, và kể cả có thêm tham số filter lúc clone thì cũng không giải quyết tận gốc. git clone là lệnh đầu tiên mà hầu như ai cũng học, nhưng lần nào cũng phải nhớ thêm filter, nếu quên thì chỉ tốn thời gian, mà ngay cả khi thành công thì repo clone về cũng có thể không hoạt động đúng. Cuối cùng phải chuyển sang mô hình như rsync, nơi các file mới nhất được lấy về trước một cách hiệu quả, mới là cách giải quyết tận gốc. git không giỏi trong việc thực hiện các thay đổi mang tính nền tảng như vậy

    • Rất đồng ý. git lúc nào cũng "giải quyết" vấn đề bằng cách thêm một cái cờ, nhưng phần lớn người dùng không biết đến các tính năng đó. Nếu cải thiện ngay ở mặc định thì vẫn có thể xử lý vấn đề mà không phá vỡ tương thích

    • repo clone về cũng có thể không hoạt động đúng
      Thực ra chỉ là thiếu lịch sử của các blob thôi

    • Bạn nói rsync giải quyết được chuyện này, vậy cụ thể nó sẽ trông như thế nào? Không phải ở mức thuật toán, mà là khi người dùng chạy git clone thì trên filesystem local thực tế sẽ xuất hiện những gì?

    • Nếu phần lớn dung lượng repo nằm ở các revision cũ, thì cách tiếp cận kiểu rsync, ưu tiên chỉ tải bản mới nhất trước, là lời giải phù hợp nhất cho đa số người dùng

  • Tôi rất vui khi hỗ trợ file lớn được thêm vào Git core. Dù là giải pháp bên ngoài thì về cơ bản cấu trúc opt-in vẫn tương tự. Chúng tôi muốn giảm số lượng lệnh tối đa và làm nó liền mạch nhất có thể, nên đã thiết kế API chỉ giới hạn trong các bộ lọc smudge/clean của file .gitattributes. Chúng tôi cũng làm việc trực tiếp với Atlassian và Microsoft để loại bỏ vendor lock-in, và API khóa file cũng nhận được nhiều hỗ trợ từ Atlassian. LFS được cung cấp dưới dạng mã nguồn mở với hỗ trợ tương thích trên ba host

  • Chúng tôi đang phát triển oxen để giải quyết các vấn đề gặp phải với git hoặc git-lfs. Nó giữ nguyên giao diện git nhưng chạy nhanh hơn nhiều trong môi trường monorepo có file lớn và hàng triệu file. Chúng tôi cung cấp CLI và server mã nguồn mở, nếu bạn quan tâm thì mong nhận được phản hồi
    https://github.com/Oxen-AI/Oxen

  • Bản thân cách git lưu trữ dữ liệu cũng cần được đại tu theo hướng content-defined chunking cho file và thư mục, giống các công cụ backup hiện đại như restic hay borg

  • Một nhược điểm khác của GitLFS bị bỏ sót là vấn đề xác thực: nếu không dùng SSH-agent thì ngay cả một lần push cũng có thể phải xác thực nhiều lần. Theo trải nghiệm trực tiếp của tôi, có khi phải làm hơn hai ba lần