跳到内容

将GitHub.com升级到MySQL 8.0

GitHub使用MySQL来存储大量的关系数据。这就是我们如何将生产团队无缝升级到MySQL 8.0的故事。

将GitHub.com升级到MySQL 8.0

15年前,GitHub最初是一个RubyonRails应用程序,只有一个MySQL数据库。从那时起,GitHub就发展了其MySQL架构,以满足平台的伸缩性和弹性需求,包括构建高可用性,实现测试自动化、和对数据进行分区今天,MySQL仍然是GitHub基础设施和我们选择的关系数据库的核心部分。

这是我们如何将1200多台MySQL主机升级到8.0的故事。在不影响我们的服务水平目标(SLO)的情况下升级车队是一项不小的成就——规划、测试和升级本身花费了一年多的时间,GitHub内的多个团队进行了协作。

升级动机

为什么要升级到MySQL 8.0?使用MySQL 5.7即将退役,我们将车队升级到了下一个主要版本MySQL 8.0。我们还想开发一个MySQL版本,该版本可以获得最新的安全补丁、错误修复和性能增强。我们还希望测试8.0中的一些新功能并从中受益,其中包括即时DDL、不可见索引和压缩bin日志等。

GitHub的MySQL基础设施

在我们深入了解如何进行升级之前,让我们先看一下我们的MySQL基础设施的10000英尺视图:

  • 我们的机队由1200多名主机组成。它是我们数据中心中Azure虚拟机和裸机主机的组合。
  • 我们存储了300多TB的数据,并在50多个数据库集群中每秒提供550万个查询。
  • 每个集群是配置为高可用性使用主副本和副本群集设置。
  • 我们的数据是分区的。我们利用水平和垂直切分来扩展MySQL集群。我们有MySQL集群,用于存储特定产品域区域的数据。我们也有水平碎片维特斯用于大域区域的集群超过了单一主MySQL集群。
  • 我们有一个由Percona Toolkit组成的大型工具生态系统,温室气体排放,协调器,弗雷诺以及用于操作车队的内部自动化。

所有这些都是一个多样化和复杂的部署,需要在维护SLO的同时进行升级。

准备旅程

作为GitHub的主要数据存储,我们坚持高可用性标准。由于我们车队的规模和MySQL基础设施的重要性,我们对升级过程有一些要求:

  • 我们必须能够升级每个MySQL数据库,同时遵守我们的服务级别目标(SLO)和服务级别协议(SLA)。
  • 我们无法在测试和验证阶段解释所有故障模式。因此,为了保持在SLO中,我们需要能够回滚到MySQL 5.7的早期版本,而不会中断服务。
  • 我们的MySQL团队的工作量非常多样化。为了降低风险,我们需要自动升级每个数据库集群,并围绕其他主要更改进行调度。这意味着升级过程将是一个漫长的过程。因此,我们从一开始就知道,我们需要能够维持混合版本环境的运行。

升级的准备工作于2022年7月开始,在升级单个生产数据库之前,我们还有几个里程碑要完成。

为升级准备基础架构

我们需要为MySQL 8.0确定适当的默认值,并执行一些基准性能基准测试。因为我们需要操作两个版本的MySQL,所以我们的工具和自动化需要能够处理混合版本,并了解5.7和8.0之间的新的、不同的或不推荐使用的语法。

确保应用程序兼容性

我们为所有使用MySQL的应用程序将MySQL 8.0添加到持续集成(CI)中。我们在CI中并行运行MySQL 5.7和8.0,以确保在延长的升级过程中不会出现退化。我们在CI中检测到各种错误和不兼容,帮助我们删除任何不支持的配置或功能,并转义任何新的保留关键字。

为了帮助应用程序开发人员过渡到MySQL 8.0,我们还启用了一个选项,在GitHub代码空间中选择一个MySQL 8.0-预构建容器进行调试,并提供了MySQL 8.0--开发集群以进行额外的预生产测试。

沟通和透明度

我们使用GitHub项目创建了一个滚动日历,以便在内部交流和跟踪我们的升级计划。我们创建了问题模板,用于跟踪应用程序团队和数据库团队的清单,以协调升级。

跟踪MySQL 8.0升级计划的项目委员会
跟踪MySQL 8.0升级计划的项目委员会

升级计划

为了满足我们的可用性标准,我们制定了逐步升级策略,允许在整个过程中进行检查点和回滚。

步骤1:滚动复制副本升级

我们从升级单个复制副本开始,并在其离线时进行监控,以确保基本功能稳定。然后,我们启用了生产流量,并继续监视查询延迟、系统指标和应用程序指标。我们逐渐将8.0个副本上线,直到我们升级了整个数据中心,然后迭代其他数据中心。为了回滚,我们保留了足够的5.7个联机副本,但我们禁用了生产流量,以便开始通过8.0服务器为所有读取流量提供服务。

副本升级策略涉及在每个数据中心(DC)逐步部署。
副本升级策略涉及在每个数据中心(DC)逐步部署。

步骤2:更新复制拓扑

在所有只读流量都通过8.0副本提供后,我们对复制拓扑进行了如下调整:

  • 一个8.0级初选候选人被配置为直接在当前5.7级初赛下进行复制。
  • 在该8.0复制副本的下游创建了两条复制链:
  • 一组只有5.7个副本的副本(不服务流量,但在回滚时准备就绪)。
  • 一组仅包含8.0个副本(用于通信)。
  • 在我们进入下一步之前,拓扑仅在短时间内(最多几个小时)处于这种状态。
为了便于升级,拓扑被更新为具有两个复制链。
为了便于升级,拓扑被更新为具有两个复制链。

步骤3:将MySQL 8.0主机升级为主主机

我们选择不在主数据库主机上进行直接升级。相反,我们将通过使用乐队指挥。此时,复制拓扑由一个8.0主节点组成,其中附加了两条复制链:回滚时的5.7个脱机副本集和8.0个服务副本集。

Orchestrator还配置为将5.7台主机作为潜在的故障转移候选主机列入黑名单,以防止在发生意外故障转移时发生意外回滚。

完成数据库MySQL 8.0升级的主要故障转移和其他步骤
完成数据库MySQL 8.0升级的主要故障转移和其他步骤

第四步:升级面向内部的实例类型

我们还有用于备份或非生产工作负载的辅助服务器。为了保持一致性,随后对其进行了升级。

步骤5:清理

在我们确认集群不需要回滚并成功升级到8.0后,我们删除了5.7台服务器。验证包括至少一个完整的24小时交通周期,以确保在高峰交通期间没有问题。

回滚能力

保持升级策略安全的核心部分是保持回滚到MySQL 5.7早期版本的能力。对于读-复制,我们确保有足够的5.7个副本保持在线以满足生产流量负载,如果8.0个副本性能不佳,则通过禁用它们来启动回滚。对于主服务器,为了在不丢失数据或服务中断的情况下回滚,我们需要能够在8.0到5.7之间维持向后数据复制。

MySQL支持从一个版本复制到下一个更高版本,但不明确支持相反的版本(MySQL复制兼容性). 当我们在登台群集上测试将8.0主机升级为主主机时,我们发现所有5.7个副本上的复制都中断了。我们需要克服几个问题:

  1. 在MySQL 8.0中,800万桶外4是默认字符集,并使用更现代的输出f8mb4_0900_ai_ci排序规则作为默认值。MySQL 5.7的早期版本支持utf8mb4_unicode_520_ci接口排序规则,但不是最新版本的Unicodeutf8mb4_0900_ai_ci.
  2. MySQL 8.0版本介绍角色用于管理权限,但MySQL 5.7中不存在此功能。当8.0实例升级为集群中的主实例时,我们遇到了问题。我们的配置管理正在扩展某些权限集,以包括角色语句并执行它们,这中断了5.7个副本中的下游复制。我们通过在升级窗口期间临时调整受影响用户的已定义权限来解决此问题。

为了解决字符排序不兼容问题,我们必须将默认字符编码设置为utf8接口和排序规则utf8_unicode接口.

对于GitHub.com,我们的Rails配置确保了字符排序的一致性,并使数据库的客户端配置更容易标准化。因此,我们对能够为最关键的应用程序保持向后复制充满信心。

挑战

在我们的测试、准备和升级过程中,我们遇到了一些技术挑战。

维特斯呢?

我们使用Vites对关系数据进行水平切分。在大多数情况下,升级Vites集群与升级MySQL集群没有太大区别。我们已经在CI中运行了Vites,因此我们能够验证查询兼容性。在我们的分片集群升级策略中,我们一次升级一个分片。VTgate,Vites代理层,宣传MySQL的版本,一些客户端行为依赖于此版本信息。例如,一个应用程序使用了一个Java客户端,该客户端禁用了5.7服务器的查询缓存,因为在8.0中删除了查询缓存,所以它为这些服务器生成了阻塞错误。因此,一旦为给定的密钥空间升级了单个MySQL主机,我们必须确保也将VTgate设置更新为advertive8.0。

复制延迟

我们使用read-replicas来扩展我们的读取可用性。GitHub.com需要低复制延迟才能提供最新数据。

在测试的早些时候,我们在MySQL中遇到了一个复制错误8.0.28修补:

复制:如果复制服务器具有系统变量复制保留提交订单=1集合在密集负载下使用了很长一段时间,实例可能会用完提交顺序票证。超过最大值后的错误行为导致应用程序挂起,应用程序工作线程在提交顺序队列上无限期等待。提交顺序序列票证生成器现在可以正确包装。感谢翟伟祥的贡献。(错误号32891221,错误号103636)

我们碰巧满足了解决这个问题的所有标准。

  • 我们使用复制保留提交订单因为我们使用基于GTID的复制。
  • 我们在许多集群上有长期的密集负载,当然也包括所有最关键的集群。我们的大多数集群都非常重写。

由于这个bug已经在上游进行了修补,我们只需要确保部署的MySQL版本高于8.0.28。

我们还发现,在MySQL 8.0中,导致复制延迟的大量写操作加剧了这种情况。这使得我们避免频繁的写操作变得更加重要。在GitHub,我们使用弗雷诺根据复制延迟限制写工作负载。

查询将通过CI,但在生产中失败

我们知道,我们将不可避免地在生产环境中第一次看到问题,因此我们的逐步推出战略是升级副本。我们遇到了通过CI但在生产中遇到实际工作负载时会失败的查询。最值得注意的是,我们遇到了一个问题在哪里子句会使MySQL崩溃。我们吃了很多在何处包含数万个值的查询。在这些情况下,我们需要在继续升级过程之前重写查询。查询采样有助于跟踪和检测这些问题。在GitHub,我们使用太阳风DPM(VividCortex)SaaS数据库性能监视器,用于查询可观察性。

学习和收获

在测试、性能调优和解决已发现的问题之间,整个升级过程花费了一年多的时间,GitHub的多个团队的工程师参与其中。我们将整个机群升级到了MySQL 8.0,包括登台集群、支持GitHub.com的生产集群以及支持内部工具的实例。此次升级突出了我们的可观察性平台、测试计划和回滚功能的重要性。测试和逐步推出策略使我们能够及早发现问题,并降低主升级遇到新故障模式的可能性。

虽然有一个逐步推出的策略,但我们仍然需要在每一步都回滚的能力,并且我们需要可观察性来识别信号,以指示何时需要回滚。启用回滚最具挑战性的方面是保持从新的8.0主副本向后复制到5.7副本。我们了解到Trilogy客户端库为我们提供了更多连接行为的可预测性,并使我们相信来自Rails主实体的连接不会破坏向后复制。

然而,对于一些连接来自不同框架/语言的多个不同客户端的MySQL集群,我们发现在几个小时内反向复制中断,这缩短了回滚的机会窗口。幸运的是,这些情况很少,而且我们没有在需要回滚之前复制中断的实例。但对我们来说,这是一个教训,即拥有已知和充分理解的客户端连接配置是有好处的。它强调了制定准则和框架以确保这些配置的一致性的价值。

之前的努力对我们的数据进行分区付费后,我们可以针对不同的数据域进行更有针对性的升级。这一点很重要,因为一个失败的查询会阻止整个集群的升级,而将不同的工作负载进行分区可以让我们逐个升级,并减少在升级过程中遇到的未知风险的影响范围。这里的折衷是,这也意味着我们的MySQL车队已经增长。

上次GitHub升级MySQL版本时,我们有五个数据库集群,现在有50+个集群。为了成功升级,我们必须投资于可观察性、工具和车队管理流程。

结论

MySQL升级只是我们必须执行的一种例行维护——对我们来说,为我们车队上运行的任何软件提供一条升级路径至关重要。作为升级项目的一部分,我们开发了新的流程和操作功能,以成功完成MySQL版本升级。然而,我们在升级过程中仍有太多步骤需要手动干预,我们希望减少完成未来MySQL升级所需的工作量和时间。

我们预计,随着GitHub.com的发展,我们的机队将继续增长,我们的目标是进一步划分数据,这将随着时间的推移增加MySQL集群的数量。构建操作任务的自动化和自我修复功能可以帮助我们在未来扩展MySQL操作。我们相信,投资于可靠的车队管理和自动化将使我们能够扩展github并跟上所需的维护,从而提供更可预测和更具弹性的系统。

该项目的经验教训为我们的MySQL自动化奠定了基础,并将为未来更高效地进行升级铺平道路,但仍需同样的谨慎和安全。

从GitHub了解更多信息

工程类

工程类

直接来自GitHub工程团队的帖子。
ReadME项目

ReadME项目

来自开发人员社区的故事和声音。
GitHub Copilot公司

GitHub Copilot公司

不要独自飞行。免费试用30天。
在GitHub工作!

在GitHub工作!

查看我们当前的职位空缺。