百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

GitHub.com 用了 17 个月,如何将 1200 台 MySQL 主机无缝升级到 8.0 版本?

wxin55 2024-11-18 17:48 8 浏览 0 评论

摘要:GitHub 使用 MySQL 存储大量关系型数据。近日,GitHub 工程团队在官方博客上发布了一篇长文,详细地分享了其如何将生产团队无缝升级到 MySQL 8.0 的故事,值得一读。

原文地址:https://github.blog/2023-12-07-upgrading-github-com-to-mysql-8-0/

作者 | Jiaqi Liu、Daniel Rogart、Xin Wu
翻译工具 | DeepL 责编 | 苏宓
出品 | CSDN(ID:CSDNnews)

15 年前,GitHub 从一个带有单个 MySQL 数据库的 Ruby on Rails 应用程序起步。从那时起,GitHub 不断发展其 MySQL 架构,以满足平台的扩展和弹性需求,包括构建高可用性、实施自动化测试和数据分区。

如今,MySQL 仍是 GitHub 基础架构的核心部分,也是其首选的关系型数据库。

在这篇文章中,GitHub 工程团队将向大家分享如何将 1200 多台 MySQL 主机升级到 8.0 版本的故事。事实上,在不影响 GitHub 服务水平目标(SLO)的情况下升级主机群并非易事。其中,无论是规划、测试,还是升级,本身就花费了一年多的时间,并且需要 GitHub 内部多个团队的通力协作。

为什么要升级到 MySQL 8.0?

随着 MySQL 5.7 生命周期即将结束,GitHub 便开始思考将系统升级到了下一个主要版本,即 MySQL 8.0。

基于最新的版本,我们可以在第一时间获得最新安全补丁、错误修复和性能增强的 MySQL 版本。同时,也能测试 MySQL 8.0 中包括即时 DDL、隐形索引(invisible index)和压缩的 bin 日志等新功能,并从中受益。

GitHub 的 MySQL 基础架构

在深入探讨如何进行升级之前,可以从上俯瞰 GitHub 的 MySQL 基础架构:

  • 机群由 1200 多台主机组成。这是 Azure 虚拟机和数据中心裸机主机的组合。

  • 50 多个数据库集群中存储超过 300TB 的数据,每秒提供 550 万次查询。

  • 每个集群都采用主集群加副本集群的高可用性配置。

  • GitHub 数据是分区的。其可以利用水平和垂直分片来扩展 MySQL 集群。同时,GitHub 有存储特定产品领域数据的 MySQL 集群,还有水平分片的 Vitess 集群,用于存储超出单主 MySQL 集群的大型领域数据。

  • 我们拥有一个庞大的工具生态系统,包括 Percona Toolkit、gh-ost、orchestrator、freno 以及用于运营机群的内部自动化工具。

所有这一切构成了一个多样而复杂的部署,需要在保持 SLO 的同时进行升级。

准备旅程

作为 GitHub 的主要数据存储工具,我们对可用性要求很高。由于 GitHub 团队规模庞大,MySQL 基础设施至关重要,因此在升级过程有一些要求:

  • 必须能够在遵守服务级别目标(SLO)和服务级别协议(SLA)的前提下升级每个 MySQL 数据库。

  • 我们无法在测试和验证阶段考虑到所有故障模式。因此,为了保持在 SLO 范围内,其需要能够在不中断服务的情况下回滚到以前的 MySQL 5.7 版本。

  • GitHub.com 的 MySQL 机群拥有非常多样化的工作负载。为了降低风险,工程师需要对每个数据库集群进行原子性升级(指的是一种将系统从一个版本或状态无缝地升级到另一个版本或状态的过程,该过程要么完全成功,要么完全失败。如果升级过程中发生故障或部分失败,系统可以回滚到之前的状态,以防止出现数据丢失或数据库处于不一致状态),并围绕其他重大变更安排升级时间。这意味着升级过程将是一个漫长的过程。因此,GitHub 工程团队从一开始就知道,需要有一个能够持续运行混合版本环境。

升级的准备工作于 2022 年 7 月开始,在升级单个生产数据库之前,也有一些提前工作需要准备。

为升级准备基础设施

我们需要为 MySQL 8.0 确定适当的默认值,并执行一些基线性能基准测试。由于当前需要运行两个版本的 MySQL,因此工具和自动化需要能够处理混合版本,并了解 MySQL 5.7 和 MySQL 8.0 之间的新语法、不同语法或废弃语法。

确保应用程序的兼容性

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

为了帮助应用程序开发人员过渡到 MySQL 8.0,GitHub 工程师还启用了一个选项,在 GitHub Codespaces 中选择一个 MySQL 8.0 预构建容器进行调试,并提供 MySQL 8.0 开发集群进行额外的预开发测试。

沟通和透明度

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

用于跟踪 MySQL 8.0 升级计划的项目板

升级计划

为了达到可用性标准,我们采取了渐进式升级策略,在整个过程中允许检查点和回滚。

步骤 1:副本(replica)滚动升级

首先,工程团队升级了单个副本,并在其仍处于离线状态时进行监控,以确保基本功能稳定。

然后,工程师启用了生产流量,并继续监控查询延迟、系统指标和应用程序指标。进而逐步将 8.0 副本上线,直到升级了整个数据中心,然后再迭代其他数据中心。

值得注意的是,GitHub 团队保留了足够的 5.7 在线副本,以便进行回滚,但我们禁用了生产流量,开始通过 8.0 服务器提供所有读取流量。

replica 升级策略涉及在每个数据中心(DC)逐步推出

步骤 2:更新复制拓扑

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

  • 配置一个 8.0 主候选副本,直接复制到当前的 5.7 主副本下。

  • 在该 8.0 副本的下游创建两个复制链:

    一组仅有 MySQL 5.7 版本副本(不提供流量,但可随时回滚)。

    一组只有 MySQL 8.0 副本(提供流量)。

  • 在进入下一步之前,拓扑在这种状态下只维持了很短的时间(最多几个小时)。

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

GitHub 选择不在主数据库主机上进行直接升级。相反,团队工程师将通过使用 Orchestrator 执行优雅故障切换,将 MySQL 8.0 副本升级为主数据库。此时,复制拓扑包括一个 8.0 主数据库和连接到它的两个复制链:一个离线的 5.7 副本(以防回滚)和一个服务的 8.0 副本。

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

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

步骤 4:面向内部的实例类型升级

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

第 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 中,utf8mb4 是默认字符集,需要使用更现代的 utf8mb4_0900_ai_ci 排序规则作为默认值。之前版本的 MySQL 5.7 支持 utf8mb4_unicode_520_ci 排序规则,但不支持最新版本的 Unicode utf8mb4_0900_ai_ci。

  2. MySQL 8.0 引入了用于管理权限的角色,但在 MySQL 5.7 中不存在这一功能。当一个 8.0 实例提升为集群中的主实例时,我们遇到了问题。我们的配置管理正在扩展某些权限集,以包含角色语句并执行它们,这破坏了 5.7 副本中的下游复制。在升级窗口期间临时调整了受影响用户的已定义权限,从而解决了这个问题。

为了解决字符排序规则不兼容问题,我们不得不将默认字符编码设置为 utf8,并将排序规则设置为 utf8_unicode_ci。

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

挑战

在整个测试、准备和升级过程中,GitHub 工程团队也遇到了一些技术挑战。

Vitess 如何?

我们使用 Vitess 对关系数据进行横向分片。在大多数情况下,升级 Vitess 集群与升级 MySQL 集群并无太大区别我们已经在 CI 中运行 Vitess,因此能够验证查询的兼容性。在分片集群的升级策略中,我们一次升级一个分片Vitess 代理层 VTgate 会公布 MySQL 的版本,某些客户端行为依赖于该版本信息。例如,一个应用程序使用的 Java 客户端禁用了 5.7 服务器的查询缓存——因为查询缓存在 8.0 中被移除,所以会产生阻塞错误。

因此,一旦给定的键空间进行单台 MySQL 主机的升级,我们就必须确保同时更新 VTgate 设置以宣传 8.0 版本。

复制延迟

我们使用读取复制来扩展读取可用性。GitHub.com 要求低复制延迟,以便提供最新数据。

在测试的早期阶段,我们遇到了 MySQL 中的一个复制错误,该错误已在 8.0.28 中得到修补:

复制:如果系统变量设置 replica_preserve_commit_order = 1 的副本服务器长期在高负载情况下使用,实例可能会耗尽提交顺序票证。超过最大值后的不正确行为会导致应用程序运行挂起和应用程序运行工作线程在提交顺序队列上无限期等待。现在,提交顺序票证生成器可以正确回绕。感谢 Zhai Weixiang 的贡献。(Bug# 32891221,Bug#103636)。

我们碰巧满足了这个 Bug 的所有标准:

  • 使用 replica_preserve_commit_order。

  • 许多集群,当然还有所有最关键的集群,都长期处于高强度负载状态。GitHub 的大多数集群都是写入量非常大的集群。

由于这个漏洞已经在上游打了补丁,我们只需确保部署的 MySQL 版本高于 8.0.28。

工程师还观察到,导致复制延迟的重写在 MySQL 8.0 中更加严重。因此,避免大量写入变得更加重要。在 GitHub,我们使用 freno 根据复制延迟来控制写入工作量。

查询可以通过 CI,但在生产环境中会失败

在生产环境中难免会首次出现问题,因此我们采取了升级副本的渐进式推广策略。我们遇到过通过 CI 的查询,但在生产环境中遇到实际工作负载时却会失败。

最值得注意的是,我们遇到了一个问题,即带有大型 WHERE IN 子句的查询会导致 MySQL 崩溃。GitHub 的大型 WHERE IN 查询包含数以万计的值。在这种情况下,我们需要在继续升级之前重写查询。查询采样有助于跟踪和检测这些问题。在 GitHub,我们使用 SaaS 数据库性能监控器 Solarwinds DPM (VividCortex) 进行查询观察。

经验教训

在测试、性能调整和解决已发现的问题,整个升级过程耗时一年多,GitHub 多个团队的工程师都参与其中。GitHub 工程团队将整个系统升级到 MySQL 8.0,包括暂存集群、支持 GitHub.com 的生产集群以及支持内部工具的实例。

这次升级凸显了 GitHub 的可观察性平台、测试计划和回滚能力的重要性。测试和逐步推出策略使大家能够及早发现问题,并降低在主要升级中遇到新故障模式的可能性。

虽然采取了逐步推进的策略,但我们仍然需要在每一步都具备回滚能力,而且需要可观测性来识别信号,以指示何时需要回滚。实现回滚的最大挑战在于保持从新的 8.0 主系统到 5.7 副本系统的向后复制。我们了解到,Trilogy 客户端库的一致性提供了更多连接行为的可预测性,并让工程师确信来自主 Rails 单体的连接不会破坏向后复制。

但是,对于一些 MySQL 集群,如果连接来自不同框架/语言的多个不同客户端,我们会发现向后复制在几个小时内就会中断,这就缩短了回滚的机会窗口。

幸运的是,这种情况很少,我们没有在需要回滚之前发生复制中断的情况。但对我们来说,这是一次教训,让大家认识到,拥有已知且易于理解的客户端连接配置是有好处的。它强调了制定指南和框架以确保此类配置一致性的价值。

之前对数据进行分区的努力取得了成效——这使我们能够对不同的数据域进行更有针对性的升级。这一点非常重要,因为一个失败的查询就会阻碍整个集群的升级,而对不同的工作负载进行分区,可以进行零散升级,减少升级过程中遇到的未知风险的影响范围。这样做的代价是,这也意味着 GitHub 的 MySQL 集群扩大了。

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

结论

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

随着 GitHub.com 的发展,工程的队伍将继续壮大,我们的目标是进一步划分数据,从而随着时间的推移增加 MySQL 集群的数量。建立自动化操作任务和自愈能力可以帮助我们在未来扩大 MySQL 的运营规模。我们相信,投资于可靠的机群管理和自动化将能够扩展 Github 并跟上所需的维护工作,从而提供一个更可预测、更有弹性的系统。

从这个项目中汲取的经验教训为 MySQL 自动化奠定了基础,并将为今后更高效地完成升级铺平道路,但仍要保持同样的谨慎和安全水平。

相关推荐

总结雅虎前端性能优化技巧(16条)

前言在日常开发中,有很多场景需要我们去做好前端优化,为了防止遗忘,加深记忆,今天参阅了一些资料以及自己的一些总结,梳理出来15条优化技巧。1.合并文件css、js合并,减少http请求数,每次http...

前端掉坑血泪史!4 个 React 性能优化绝招让页面秒开

在前端圈子里摸爬滚打这么多年,我发现React开发时踩坑的经历大家都大同小异。页面加载慢、组件频繁重渲染、状态管理混乱……这些痛点,相信不少前端工程师都感同身受。别愁!今天就给大家分享4个超...

Qwik:革新Web开发的新框架

听说关注我的人,都实现了财富自由!你还在等什么?赶紧加入我们,一起走向人生巅峰!Qwik:革新Web开发的新框架Qwik橫空出世:一场颠覆前端格局的革命?是炒作还是未来?前端框架的更新迭代速度,如同...

大模型服务平台百炼使用

提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。一、通过变量的方式使用平台模板一个好的Prompt可以更好的让模型理解我们的需求,产...

Vue应用性能优化实战:8 个提升页面加载速度的关键策略

一、构建优化与代码精简1.1代码分割与异步加载路由级代码分割:使用动态导入语法拆分路由组件组件级懒加载:结合Suspense实现按需加载javascript//vue-router4.x配置...

前端里那些你不知道的事儿之 【window.onload】

作者:京东科技孙凯一、前言相信很多前端开发者在做项目时同时也都做过页面性能优化,这不单是前端的必备职业技能,也是考验一个前端基础是否扎实的考点,而性能指标也通常是每一个开发者的绩效之一。尤其马上接近...

谷歌站长后台的“核心网页指标”不合格先优化哪个最有效?

根据对上千个网站案例的分析,90%的站长在修复时都陷入“盲目优化”误区——要么死磕服务器配置却忽略图片规范,要么过度压缩JS反而引发CLS布局错位。事实上,移动端页面抖动(CLS)才是60%中小网站的...

Vue3 开发效率拉胯?这 10 个技巧让你开发速度翻倍!

写Vue3项目时,是不是经常被数据更新延迟、组件间传值混乱、页面卡顿这些问题搞得焦头烂额?别担心!今天带来10个超实用的Vue3实战技巧,全是从真实项目中总结出来的“血与泪”经验,帮你...

2024年的JavaScript性能优化:仍然重要吗?

#记录我的9月生活#在不断发展的Web开发领域,新的JavaScript框架和库令人眼花缭乱,很容易让人忽视一些基本的东西。但在这股兴奋之中,性能作为一个卓越用户体验的基石,不能被忽略。为什么?因为...

JS 图片简易压缩【实践】

作者:政采云前端团队转发链接:https://juejin.im/post/5ea574cc518825736e57fcca前言说起图片压缩,大家想到的或者平时用到的很多工具都可以实现,例如,客户端类...

Vue3 开发总踩坑?这 10 个技巧让你少走半年弯路!

前端开发的路上,Vue3虽然强大,但坑也不少!性能优化总没效果?复杂组件通信一头雾水?别担心!今天分享10个超实用的Vue3实战技巧,全是一线开发总结的经验,帮你轻松避开开发雷区,效率直接拉...

前端分享-Vue首屏加载优化

首屏加载速度直接影响用户留存率——当加载时间超过3秒,53%的用户会直接离开(网上来的数据)。Vue单页应用尤需重视,因为传统打包方案会将所有资源打包成巨大的vendor.js,导致用户首次访问时像下...

Core Web Vitals 变了,网站性能这件事得重新关注

现在做网站优化,不能只看速度条,不管你是搞外贸独立站,还是给品牌建站,体验页面这件事你迟早得面对。谷歌这两年把网站的“体验感”提得越来越多,尤其是CoreWebVitals(网页核心指标)一出来,...

页面卡顿到崩溃?5 个实战技巧让前端性能飙升 80%!

作为前端工程师,你有没有遇到过这种情况:精心开发的页面,一上线就被用户吐槽卡顿、加载缓慢,甚至频繁崩溃。明明代码逻辑没问题,可性能就是上不去,这到底是哪里出了问题?别着急,今天就来分享5个超级实用...

周末复习前端js基础知识点总结一,记录完之后好复习(大佬勿喷)

一、深浅拷贝知识1、基本数据类型只有赋值没有拷贝2、数组和对象的赋值是浅拷贝3、结构赋值是深拷贝还是浅拷贝?二、实现深拷贝的几种常用方法方法1、通过json方法深拷贝方法2.基本的封装深拷贝的方法采用...

取消回复欢迎 发表评论: