通往代理级别Git基础设施的桥梁
我们将99.99999%的GitHub存储库的副本迁移到了Azure。接下来,我们将介绍每个引用法定人数是如何将其余存储库带过来的,以及对象存储如何让读取独立扩展。2026年10月21日 | 8分钟分享:在过去的一年里,尽管Git流量呈指数级增长,GitHub上的99.99999%的Git存储库开始同时存在于两个地方:我们自己的数据中心和Azure。其余的则是在平台上最繁忙的存储库,它们受到了一件事的限制:25毫秒的往返时延。在之前的一篇博客文章中,我们介绍了下一代存储架构背后的两个原则:最小化协调,将存储与计算解耦。在这篇文章中,我们将看看我们旧设计和新设计之间的直接桥梁。我们将讨论更改法定人数序列化写入方式如何将最繁忙的存储库带入Azure,以及同一步骤如何为没有自己持久状态的副本打开大门。我们是如何走到这一步的:迁移的时间和形状是由代理变革驱动的:流量快速增长,数据中心及其周边地区的电力容量达到硬性上限,以及物理供应链限制。在升级过程中,剩余的容量与所有可能的扩展为我们提供了足够的时间进行安全迁移,但没有足够的时间让我们慢慢来。虽然我们为在云基础设施上扩展Git的新的架构方法进行了原型开发,但在这种增长下,储存架构和托管平台同时进行变更风险过大。考虑到我们的规模以及该服务对社区的重要性,我们需要仔细安排工作:首先迁移数据,然后在数据到达后演变构架。Git操作是一项Tier 0服务:它是GitHub的核心功能,GitHub几乎所有内容都依赖于存储库数据。这意味着数据、服务和每个依赖项都必须首先迁移。第一个存储库副本于2025年8月落户于Azure的中部美国地区。如今,大多数存储库在Azure中有五个副本中的两个,而Azure大约为一半的Git流量提供服务。在大多数时间里,Azure吸收了我们的增长,而我们自己的数据中心则保持稳定。在迁移期间,流量跟随数据。大多数存储库有五个副本,以平衡耐久性、可用性和扩展性。为了最小化读取延迟,Spokes使用优先路由将流量引导到与请求相同区域和可用性区域的副本。随着副本进入Azure,每一个副本的到来,使得可以由Azure服务的流量份额随之增长,直到最后一个副本到达,旧地区完全停止接收请求。每个存储库都经历这个过程:没有存储库被遗忘。那么,剩余的0.00001%的存储库呢?在我们当前的架构中,托管在多个区域的存储库的写入吞吐量上限更低。Spokes的法定人数只需要绝大多数来提交写入,但在移动之前,它需要等待每个健康副本响应(最多到超时)。因此,慢副本会延误每个写入,而不是落后。实际上,这意味着最慢的健康副本限制了每个写入的延迟,并且因为每个写入会在提交之前保持一个跨存储库的锁,所以它也限制了吞吐量上限。一旦副本位于目标Azure区域,它距离我们的数据中心大约1000英里,使得每个网络往返大约需要25毫秒,并且写入需要多个往返。已经靠近其吞吐量上限的存储库无法在每次序列化写入时吸收这种延迟;随着每次推送,它会进一步落后。在这里,我们的第一个原则——最小化协调——发挥了作用。特别针对这种情况:不要序列化不需要的内容。Git的一致性要求适用于每个引用。一旦服务器接受了某个引用的更新,任何客户端都不应再看到较旧的值,只能看到该值或更新的值。无论更新是快进、强制推送,还是删除,这一原则都适用。但是,在大多数情况下,不同引用之间的更新并不相互依赖,可以独立应用。因此,我们正在将法定人数的范围从整个存储库更改为单个引用。在整个引用更新期间(包括往返),保持一个跨存储库的锁会将法定人数转变为一个单一的、无序的请求队列,每次只有一个写入在飞行中。该队列中的每个写入的持续时间取决于最慢的健康副本。随着写入速率的增加,队列也在增长,每个写入需等待的时间比前一个更长。在每个引用序列化下,只有在共享引用上有竞争的写入需要相互等待。在实践中,大多数对存储库的写入涉及不同的引用,例如,代理和开发者推送到他们自己的分支,因此它们不再相互等待。每个写入仍将经历跨区域延迟,但对不同引用的写入可以并行处理。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡