<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          帶你了解Git大倉庫

          共 8839字,需瀏覽 18分鐘

           ·

          2022-07-28 00:24

          前言

          Git 是目前世界上最為廣泛使用的軟件版本控制系統(tǒng)(Version Control System),同時也是一個成熟及活躍的開源項目。

          在git-scm.com點擊git圖標,你可以看到如下幾句話:

          1. --fast-version-control (快捷的版本控制)
          2. --local-branching-on-the-cheap (廉價的本地分支)
          3. --distributed-is-the-new-centralized (分布式是新的集中式)
          4. --distributed-even-if-your-workflow-isnt(分布式,且與你的工作流無關(guān))
          5. --everything-is-local(一切本地化)

          Git 最初是由 Linux 之父 Linus Torvalds 在 2005 年創(chuàng)建,截至目前共有1500多名貢獻者參與其中。

          Git使用范圍雖廣,但仍有一個普遍的共識:Git并不適合用于維護巨型的倉庫。接下來,讓我們一起來深入了解一下其中的原因,以及有哪些可以優(yōu)化的手段。

          1.什么是大倉庫?

          提到大倉庫,大家通常會聯(lián)想到單倉Monorepo。

          業(yè)內(nèi)最出名的幾家monorepo實踐分別是:

          • Google,基于 Perforce[1] 開發(fā)而來的Piper。
          • Facebook,基于 Mercurial[2] 定制化實現(xiàn)。
          • Microsoft,基于 Windows 虛擬文件系統(tǒng)及 Git 開發(fā)而成的 GitVFS[3] 。

          以上這些 Monorepo 實踐,無一不依賴內(nèi)部/特定的基礎(chǔ)設(shè)施;并且,想要落地并不是解決了代碼托管這一個問題就可以萬事大吉,還需要整個研發(fā)生態(tài)能力的支持。

          在Monorepo基礎(chǔ)設(shè)施建設(shè)尚不完善的今天,大多數(shù)廠商都選擇基于Git來嘗試探索Monorepo實踐。那么,讓我們把焦點聚焦在Git單體大倉庫(即單體大于100GB)。

          2.Git大倉庫帶來哪些挑戰(zhàn)?

          2.1 存儲挑戰(zhàn)

          我們第一個要面臨的就是如何存一個巨大的Git單倉。

          在通常的認知當(dāng)中,Git倉庫是一個完整的個體(不考慮shallow clone的情況下)。Git 之所以稱為分布式版本控制系統(tǒng),也正是由于它區(qū)別于 SVN 等中央版本控制系統(tǒng), 其最大差異點在于,在每一個倉庫的使用者那里,都維護了完整的 Git 倉庫數(shù)據(jù),任何人都可以選擇將自己本地的內(nèi)容作為中心副本來維護。

          在今天,不論是我們的個人電腦還是服務(wù)器,存儲容量都已經(jīng)大幅升級;所以看來,將100GB的倉庫存儲到本地,似乎并不是什么難題。那假如,類比Google PB(1PB=1024TB)級別的代碼資產(chǎn),都存儲到Git當(dāng)中,你是否仍然可以把它下載到本地呢(??)?答案顯然是否定。

          有人會說,那使用共享存儲(如NAS),是否可以解決存儲的問題呢?

          答案是該方案會遇到較大的性能挑戰(zhàn),讓我們一起往下看。

          2.2 性能挑戰(zhàn)

          性能挑戰(zhàn)主要在讀和寫兩個方面,讓我們先來看看寫性能挑戰(zhàn)。

          2.2.1 并發(fā)協(xié)作(寫)

          如果一個倉庫只是幾個人維護,那即使這個倉庫的體積變得非常大,有些問題還是可以忍受的;但挖大坑(??) 的,往往是一個很大的團隊(數(shù)千人,甚至數(shù)萬人)。

          以千人實踐主干模式為例,你可能需要:

          • 清晰的管理方案:上千個松散引用(引用 reference refs/*,常見的分支branch refs/heads/*及標簽tag refs/tags/*都屬于引用的范疇)來維護每個人的特性。
          • 更為合理的多副本架構(gòu),來解決單點負載不足問題。
          • 一個良好的Checkin機制,來解決root tree爭奪問題。
          • 更為順暢的垃圾回收機制,避免倉庫陷入無限GC循環(huán)(默認6700個松散對象觸發(fā)autogc,可能一個GC還未結(jié)束,又一次新的GC在路上了)。

          2.2.2 大范圍查找(讀)

          在前面的圖中,我們也可以看到,Git是通過commits樹來串聯(lián)整個版本歷史的。在不借助額外的索引的情況下,可以認為git的對象是離散存儲在文件當(dāng)中的。

          Git的對象存儲包含兩個部分:

          1. 松散對象:存儲在 objects/xx 目錄下,每個文件就是一個對象。
          2. 包文件(packfile):存儲在 objects/pack 目錄下,相較于松散對象,pack是一組對象的集合,擁有一個索引文件 pack-xxx.idx;當(dāng)然,在倉庫較大的情況下,還會包含多個打包文件。
          ?  objects git: tree
          .
          ├── 03
          │ └── 273f5843529db977846d7c6fd28dc790123d38
          ├── 7f
          │ ├── ec94d35df31a1deb570f8b863526a27f148f48
          │ └── ff37186bcf8a8f5428aa168f981c9094bef2e6
          ├── info
          └── pack
          ├── pack-0c63ce8bd48a11517c3f1775d9060d45c088afc5.idx
          ├── pack-0c63ce8bd48a11517c3f1775d9060d45c088afc5.pack
          ├── pack-47155f8be24f5b6666bf849d681f831d5f34bffe.idx
          └── pack-47155f8be24f5b6666bf849d681f831d5f34bffe.pack

          查找指定對象的過程,如 git cat-file -t xxx。

          如圖所示,在上述沒有多包索引的倉庫中,如果我們想要根據(jù)一個hash值來查找指定對象,首先需要遍歷松散對象目錄查找是否存在于松散對象,而后再逐個查詢打包文件,在打包文件較多的情況下,逐個遍歷索引的效率也并不高。

          如果你說,性能問題我忍了,那我是不是可以高枕無憂(??)?答案也是否定的。

          2.3 穩(wěn)定性挑戰(zhàn)

          二八定律同樣也適用在代碼托管領(lǐng)域。

          而極為少數(shù)的單體大倉庫,往往能得到額外的優(yōu)待:

          1. 獨享的機器資源
          2. 更多的技術(shù)支持占用
          3. 更高的技術(shù)關(guān)注度

          人肉炸彈??——某次生產(chǎn)環(huán)境代碼存儲節(jié)點上的內(nèi)存使用變化:

          而類似問題,可能還需要在Git當(dāng)中進行優(yōu)化才能得以解決,參考《unpack-objects: support streaming blobs to disk》[4]。

          2.4 可靠性挑戰(zhàn)

          讓我們再來思考一個問題,對于存儲類的服務(wù),我們要守住的底線是什么?

          我認為,必須要守住的底線是數(shù)據(jù)完整性。我們可以接受一定時間的服務(wù)不可用,我們可以通過各種手段,提升我們的服務(wù)可用率;但若是出現(xiàn)數(shù)據(jù)完整性問題,那對用戶帶來的影響是更加大的。

          雖然Git自帶的hashsum,可以解決數(shù)據(jù)完整性校驗問題,但解不了所有問題,因為:

          1. Git是IO密集
          2. 大倉庫加劇了IO消耗

          3.如何應(yīng)對Git單體大倉庫?

          看完了問題,讓我們再來看看有哪些解決方案。

          3.1 事前預(yù)防

          3.1.2 如何控制倉庫膨脹?

          在不考慮 submodule 及 Git-repo[5] 進行邏輯拆分的情況下,如何控制倉庫的體積?

          我們先來看,導(dǎo)致倉庫體積超過預(yù)期的原因都有哪些?

          1. 大型的二進制文件(如圖片資源、可執(zhí)行程序、Office文檔等)。
          # 取top20的大文件
          git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -20 | awk '{print$1}')"

          # 取大于500k的大文件
          git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | awk '{if($3>500000)print $1}')"
          1. 大量的無用引用(如早先版本的gitlab在添加文件等場景下會創(chuàng)建tmp引用,并缺少清理機制)。

            • git的GC機制僅會移除不可達的對象(不存在于任何一個引用上)。
          2. 龐大的文件數(shù)據(jù)及版本記錄(針對這個問題,我們需要區(qū)分對待)。

            • 例如:iOS的應(yīng)用依賴管理工具Cocoapods。

          對癥下藥:

          1. 對于二進制文件,可以借助 https://github.com/git-lfs/git-lfs[6],將文件上傳到對象存儲。

            • Git-lfs 的落地,依賴客戶端的安裝,存在一定的成本,但確是上選。
            • 對于非預(yù)期的提交,添加 pre-commit hook 做本地攔截也是一個好選擇。
            • 活用.gitignore,排除編譯產(chǎn)物、非必要依賴等的提交。
          2. 引用清理:

            • 本地經(jīng)常性進行開發(fā)分支清理并GC是一個不錯的選擇。
          3. 選擇更合理的存儲服務(wù):

            • 對版本要求不高的場景,對象存儲(Object Storage,如火山云產(chǎn)品TOS[7])的成本更為低廉。

          3.1.3 如何高效識別用戶大文件提交?

          行業(yè)內(nèi)的普遍做法

          通過pre-receive hook,對隔離區(qū)(Quarantine,objects/incoming-xxxx)中的對象大小進行識別,其中松散對象可以通過文件頭中的size來判斷,packfile則通過git verify-pack

          但是這個方案的效率并不高:

          1. 需要遍歷隔離區(qū)的所有對象,事先并不知道哪些對象是commit、哪些是blob。
          2. verify-pack的核心用途是校驗packfile的完整性,對讀取完整的數(shù)據(jù);而我們的場景,只需求文件大小。
          更優(yōu)的方案

          在2021年11月與來自Github的Peff(Jeff King)的交流中,得到了新的啟發(fā):

          https://lore.kernel.org/git/[email protected]/

          We also set GIT_ALLOC_LIMIT to limit any single allocation. We also have custom code in index-pack to detect large objects (where our definition of "large" is 100MB by default):   - for large blobs, we do index it as normal, writing the oid out to a    file which is then processed by a pre-receive hook (since people    often push up large files accidentally, the hook generates a nice    error message, including finding the path at which the blob is    referenced)   - for other large objects, we die immediately (with an error message).    100MB commit messages aren't a common user error, and it closes off    a whole set of possible integer-overflow parsing attacks (e.g.,    index-pack in strict-mode will run every tree through fsck_tree(),    so there's otherwise nothing stopping you from having a 4GB filename    in a tree).

          我們可以在執(zhí)行 index-pack / unpack-objects 的過程中,將對象的oid、類型、大小記錄在額外的文件中,在后續(xù)pre-receive hook執(zhí)行的時候,就可以根據(jù)已有的結(jié)果來做展示信息加工。在這個過程中,無需再遍歷所有的對象及packfile整體,復(fù)用了數(shù)據(jù)接收過程,這對大型倉庫的效率提升是顯著的。

          3.2 事中提效

          3.2.1 如何下載一個Git大倉?

          通常會遇到如下問題:
          1. 引用發(fā)現(xiàn)慢
          2. 對象計算久
          3. 網(wǎng)絡(luò)不穩(wěn)定中斷
          可以怎么做:
          1. 使用 protocol version 2

          以 https://github.com/kubernetes/kubernetes 為例,如果下載該倉庫的全量引用,總共有近10w,而如果僅關(guān)心 branches 及 tags,那么僅有不到 1k。

          10:39:01.435687 pkt-line.c:80           packet:        clone> ref-prefix HEAD
          10:39:01.435692 pkt-line.c:80 packet: clone> ref-prefix refs/heads/
          10:39:01.435696 pkt-line.c:80 packet: clone> ref-prefix refs/tags/

          Git 在 2.18以后就開始支持新的協(xié)議,在更新的版本中,更是將 v2 作為默認協(xié)議,在這個協(xié)議下可以有更好的表達空間。

          如果你的Git版本不高,可以考慮增加設(shè)置使用v2:

          git config --global protocol.version=2
          1. 使用 shallow clone

          如果你不那么關(guān)心歷史版本,shallow clone是一個不錯的選擇。

          git clone --depth=100 [email protected]:kubernetes/kubernetes.git
          1. 使用 partial clone[8]

          比如我也想試試本地維護一個linux,同時也想看看這個擁有100w個commits倉庫的演進歷史,我可以這么做:

          git clone --filter=blob:none [email protected]:torvalds/linux.git
          1. 使用bundle

          Git當(dāng)中,提供了將所有對象及引用打包的能力 git bundle,借助對象存儲及CDN,可以對文件進行分段讀取,在網(wǎng)絡(luò)條件不好的情況下,真的可以救命。

          目前Git社區(qū)的 Derrick Stolee 及 ?var Arnfj?re Bjarmason 正在推進bundle uri能力的落地,這將更好地改善大倉庫的下載體驗。

          https://lore.kernel.org/git/[email protected]/

          3.2.2 如何在減小本地工作空間

          好不容易把一個Git的倉庫下載下來,往往檢出又成了難題。

          Git倉庫中的文件都是經(jīng)過壓縮的,而解壓縮之后,體積往往成倍膨脹開來;而對于一個大庫,我們可能只關(guān)心其中的某個路徑,這時候,就輪到 git sparse-checkout 登場了。

          下圖摘自:[《Bring your monorepo down to size with sparse-checkout》9]

          順帶一提,微軟的大倉庫客戶端scalar目前也已經(jīng)進入git的子項目進行孵化,后續(xù)可以在git當(dāng)中使用。

          https://github.com/git/git/tree/master/contrib/scalar

          Scalar is an add-on to Git that helps users take advantage of advanced performance features in Git. Originally implemented in C# using .NET Core, based on the learnings from the VFS for Git project, most of the techniques developed by the Scalar project have been integrated into core Git already:

          • partial clone,

          • commit graphs,

          • multi-pack index,

          • sparse checkout (cone mode),

          • scheduled background maintenance,

          • etc

          3.2.2 如何提升訪問效率?

          在前文“2.2 性能挑戰(zhàn)”當(dāng)中,我們已經(jīng)了解了Git如何存儲及查找對象的方式。

          對于Git訪問來說,影響訪問效率的核心在于需要解決兩個問題:

          1. 這個對象是什么?
          2. 這個對象和其他對象的關(guān)系是什么?

          Git大倉庫的性能優(yōu)化,一直也來也是社區(qū)非常關(guān)注的問題,為此,社區(qū)引入了幾個特性:

          1. 回答關(guān)系的問題:

            • Git當(dāng)中引入了commit-graph[10],通過記錄commit的root tree、parents、date等信息,加速了commits遍歷的效率。Commit-graph 可以通過 core.commitGraph 配置進行開啟。

          2. 回答是什么的問題:

            • Git當(dāng)中引入了bitmap[11],通過 Commits、Trees、Blobs、Tags 4個位圖,在無需讀取對應(yīng)對象的頭信息的情況下,就可以知道對象的類型及位置信息。

          這里有人會說,bitmap 只能解決 單個packfile的場景,多個packfile就失效了。

            • 在多個packfile的情況下,core.multiPackIndex 開啟多包索引,進行packfile的索引合并,也可以加速對象索引的過程。

            • 此外,在Git的 v2.34.0 當(dāng)中,引入了multi-pack-bitmap,至此針對于多包場景下的幾何打包策略(geometric repack,參見《Scaling monorepo maintenance》[12])開始登上舞臺。

          3.3 事后優(yōu)化

          3.3.1 優(yōu)化存量倉庫

          坦言,今天對于合理發(fā)展的存量大倉庫,并沒有太多可以瘦身的手段。

          而對于“3.1.2 如何控制倉庫膨脹?”中提到的不合理的場景,我們可以借助 git filter-branch 等手段進行歷史版本重寫,移除其中的大對象。

          這個操作本身存在較大的風(fēng)險,并且在版本重寫之后,所有用戶本地的副本也要重新從遠端拉取,在執(zhí)行成本上也是非常高的。

          3.3.2 增量冷備

          bundle冷備無論是為了bundle uri能力預(yù)設(shè),還是預(yù)防非預(yù)期問題上,都是代碼安全能力建設(shè)的一道重要防線。而在傳統(tǒng)的全量備份方案上,Git單體大倉極大提升了備份的周期及難度。

          而隨著增量bundle能力的支持,針對大倉庫的冷備也不再那么困難,我們可以基于先前的備份做增量補丁。

          增量bundle支持:https://lore.kernel.org/git/[email protected]/

          小結(jié)

          我們從Git單體大倉庫入手,了解了其帶來的存儲、性能、可靠性等方面的挑戰(zhàn),并且我們從事前、事中、事后三個角度提出了針對一些問題的解決/優(yōu)化方案。

          當(dāng)然,今天所提到的也只是幾個比較經(jīng)典的問題,還有更多的問題及解決方案等待我們在實踐中持續(xù)探索。

          附錄

          1. Perforce:https://www.perforce.com/

          2. Mercurial:https://www.mercurial-scm.org/

          3. VFSForGit:https://github.com/microsoft/VFSForGit

          4. 《unpack-objects: support streaming blobs to disk》:https://lore.kernel.org/git/[email protected]/

          5. git-repo:https://gerrit.googlesource.com/git-repo

          6. git-lfs:https://github.com/git-lfs/git-lfs

          7. TOS:https://www.volcengine.com/product/tos

          8. partial clone:https://git-scm.com/docs/partial-clone

          9. 《Bring your monorepo down to size with sparse-checkout》:https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/

          10. commit-graph:https://github.com/git/git/blob/master/Documentation/technical/commit-graph.txt

          11. bitmap:https://github.com/git/git/blob/master/Documentation/technical/bitmap-format.txt

          12. Scaling monorepo maintenance:https://github.blog/2021-04-29-scaling-monorepo-maintenance/

          - END -


          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青娱乐-亚洲高清视频在线观看 | 日本色情在线 | 国产高清无码福利 | 欧美人精品人妻在线 | 国产手机精品伦子伦 |