用Git虚拟文件系统来解决大型存储问题

Ste6229 7年前
   <p>尽管大家都认为Git是广为采用的最好的版本控制软件,但它仍然远远不够完美。有些问题可以用第三方工具来解决,但要把整个代码库都复制到开发者的电脑上时却可能会坏事。微软在试图将他们的300GB的代码库从内部系统迁移上Git时发现了这个问题。因此催生了Git虚拟文件系统(Git Virtual File System,GVFS)。</p>    <p>故事要从大概2000年左右开始讲起,当时微软是主要在使用一套名为“Source Depot”的内部系统,这是Perforce的一个分支。慢慢地,许多团队都换用了TFVC,即Team Foundation Server(TFS)里原来用的版本控制系统,但许多大型团队却没办法判断要花多少代价才能从Source Depot上迁移出来。同样,许多团队都是使用了TFS的部分功能,但具体用的是哪一部分却因团队而异,还各自混用了许多第三方工具及内部开发的工具。</p>    <p>为了努力简化这样复杂的环境,微软决心将用于工作计划、自动构建和源码控制等的Visual Studio团队服务(即云上TFS)相关的许多团队标准化。其中的最后一点,就是和Git一起提供的VSTS。</p>    <p>为什么是Git呢?根据微软员工,以及reddit的用户 jeremyepling 的想法,主要有三点原因:</p>    <p>Git和GitHub上的公共存储都是OSS开发的事实标准。微软做了许多OSS开发,我们想让我们的TFS和团队服务等DevOps工具可以和这些工作流一起工作得很好。</p>    <p>我们希望微软里所有团队都使用相同的版本控制系统。标准化将使得人们在项目之间转换,以及形成资深专业经验的过程变得容易。因为OSS是捆绑到Git上的,而且我们也做了许多OSS开发,那Git就自然而然的成为我们的首选了。</p>    <p>我们希望能响应并支持社区和我们的DevOps客户希望的方向。Git很明显就是现代版本控制系统的领头羊。</p>    <p>但如 Brian Harry所说 ,这有一些问题:</p>    <p>关于选择Git有许多争论,最主要的一个是规模问题。没有多少公司会有我们这么大规模的代码库。特别是Windows和Office(还有一些其它的),规模非常巨大。有几千个工程师,几百万个文件,几千台构建服务器在不断的构建它——老实说,这是令人难以置信的。说得更清楚些,当我在这篇贴子中提到Window的时候,我实际说的是一个非常模糊的概念,这包括了用于PC、手机、服务器、HoloLens、Xbox、物联网等等方面的所有Windows系统。而Git是一个分布式的版本控制系统(distributed version control system,DVCS)。它会把整个代码库和所有的文件历史都拷到你自己的机器上。要是对Windows也这么做的话会被人笑话的(事实上我们也的确被别人笑话了很多次)。TFVC和Source Depot都针对大型代码库和团队做过非常专门的优化。Git在这样的问题上却从来没有过先例(哪怕类似规模的也行),所以很多人都断定这种方法肯定行不通。</p>    <p>Reddit用户Ruud-v-A为这个问题提供了一些参考信息:</p>    <p>Linux内核的代码库有1.2GB了,开发了大约12年,有5700个文件。2005年第一次提交时记录过,将整个开发历史都导入进来一共是3.2GB。3.2GB对应5700个文件,由此推算那350万个文件就会需要270GB。</p>    <p>Chromium的代码库(包含了Webkit从2001年开始的历史代码)大小时11GB,有24.6万个文件。由此推算要20年并且有350万个文件的话,需要196GB的空间。</p>    <p>将Windows代码库拆分成合适大小的子代码库这条路也行不通。假如当初一开始的时候就是这么做的,那还有可能,可是到现在代码库已经这么大了,而且又发展了这么久,要再想回头把它拆分开,这事实上并不可能了。Brian继续说:</p>    <p>这意味着我们必须开始着手将Git增强,让它可以支持几百万个文件,几百G的大小,可以由几千个开发者一起使用。同时提供一点参考信息,即使是Source Depot也没办法支持得了整个Windows的代码库规模。它被拆分成了40多个代码库,所以我们才能将它扩展,但必须在它们之上构建一层,这样在许多情况下,才能像使用一个代码库一样使用它们。抽象当然并不完美,有时也会造成一些冲突。</p>    <p>为什么不干脆把历史版本都丢掉,然后重新开始呢?SuperImaginativeName给出了一个说法:</p>    <p>NT内核、NT的驱动、子系统、API、硬件驱动、Win32 API等,全都被其它系统所依赖,包括客户。你觉得为什么你可以在Windows上运行一个已经开发了30年的程序呢?没有这些历史版本,以内核团队为例,他们就不会记得15年前在一款特别的CPU上必须设置一个特殊的标记位,因为它的指令集架构中有个小BUG,会导致用户无法正常运行某个旧的应用程序。如果把这些历史版本丢掉了,那也就意味着丢掉了非常大量的信息。你总不能期望每个开发者都能用脑子记住为什么一个特别的系统是那样工作的。能想像某段奇怪的代码看起来好象不怎么正确,但事实上它却能让你免于遭受文件损坏之苦吗?如果只是简单地提交一个新补丁,再注释上“解决了一个奇怪的问题,真不明白为什么以前没人发现它”,这么做的后果会是灾难性的。再想想去查看一下代码历史,然后最终恍然大悟原来这么做是有道理的,这两种行为哪个才更正确?Windows代码的开发是非常严谨的,所有东西都是要经过审核的。</p>    <p>在经过了若干次失败的尝试,包括尝试使用Git子模块等之后,微软开始开发Git虚拟文件系统了:</p>    <p>我们试过一种“虚拟化”Git的方案。通常当你用Git克隆分支时,它会把所有东西都下载下来。但如果它不下载所有东西,又会怎样?如果我们可以把底层的存储虚拟化,让它只下载需要的东西,又会怎样?这样的话克隆一个巨大的300GB的代码库就会非常快了。当我执行Git命令,或者在我的文件系统里读写文件时,系统会无缝地从云端获取内容(并且将它保存在本地,这样以后就是本地访问数据了)。这种方法的缺点在于没有离线支持。如果你需要这一点,你就得把所有东西“touch”一遍,假装在本地有它们,除此之外没有任何其它缺点了——你仍然会有100%一致的Git体验。而且对于我们的巨型代码库来说,这也是行得通的。</p>    <p>Saeed Noursalehi 补充到:</p>    <p>有了GVFS,就意味着他们现在可以有了更好管理的Git使用体验:每次克隆只需要花几分钟,而不用12多个小时;检出代码只需要大约30秒,而不是2-3小时;status命令也只需要4-5秒钟,而不是10分钟。而且我们还在不断努力,让它些数字变得更好(当然,代价是第一次构建会花的时间更多些,因为它要把它构建的所有文件都下载下来,但以后再构建就不会比正常构建更慢了)。</p>    <p><strong>微软对于Git的投入</strong></p>    <p>要让这种方法可以正常工作,就要改进Git访问文件的方式。大家平时不一定会注意到,旧版的Git在访问本地存储的文件时,它通常会扫描比它真正需要的更多的文件。如果你去年已经注意到微软曾向Git OSS项目提交过改进性能的代码,这就是原因所在了。</p>    <p>Jeremyepling写道:</p>    <p>我们微软Git团队为git/git和git-for-windows做了许多贡献,来提高Git在Linux、Mac和Windows等上面的性能。在Git 2.10中,我们做了许多工作来使交互式Rebase操作更快。根据包含在Git源代码中的一种基准测试结果看来,交互式Rebase的最终表现是在Windows速度提升了5倍,在MacOSX上提升了4倍,在Linux也还有3倍的提升。</p>    <p>对Git的一些改进列举如下:</p>    <ul>     <li>sha1:在mingw上使用openssl sha1程序 <a href="/misc/goto?guid=4959739424811102352" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/915</a></li>     <li>preload-index:对于跳过worktree的元素不使用lstat <a href="/misc/goto?guid=4959739424904562111" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/955</a></li>     <li>memihash perf <a href="/misc/goto?guid=4959739424982277377" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/964</a></li>     <li>add:用预加载索引和fscache来提高性能 <a href="/misc/goto?guid=4959739425068839046" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/971</a></li>     <li>read-cache:在后台线程中运行verify_hdr() <a href="/misc/goto?guid=4959739425149205399" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/978</a></li>     <li>read-cache:在检出过程中提高add_index_entry速度 <a href="/misc/goto?guid=4959739425228787673" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/988</a></li>     <li>string-list:在重新分配string_list时使用ALLOC_GROW宏 <a href="/misc/goto?guid=4959739425314134579" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/991</a></li>     <li>diffcore-rename:提高register_rename_src的速度 <a href="/misc/goto?guid=4959739425399921372" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/996</a></li>     <li>fscache:将找不到的目录缓存加入fscache <a href="/misc/goto?guid=4959739425482134309" rel="nofollow,noindex">https://github.com/git-for-windows/git/pull/994</a></li>    </ul>    <p><strong>Git虚拟文件系统</strong></p>    <p>Git 虚拟文件系统 GVFS的原型是在客户端用一个文件系统驱动,以及一个支持GVFS的Git版本实现的。这需要“Windows 10周年”版,或更新的版本。只要Git库支持了GVFS,那平时常用的Git命令就仍可以照常使用。GVFS子系统基于文件系统工作,它会在背后从服务器下载任何你需要的文件。</p>    <p>因为GVFS客户端库是开源的,这也是大家研究我们是如何在Windows上把虚拟文件系统实现为驱动的好机会。</p>    <p>在服务器端,你需要一些实现 GVFS协议 的东西。现在,这指的是Visual Studio团队服务,这协议是开源的,所以别的服务提供商也可以提供相同的能力。GVFS协议本身也是很简单的,包含四个类REST的接口。</p>    <p> </p>    <p> </p>    <p>来自:http://www.infoq.com/cn/news/2017/02/GVFS</p>    <p> </p>