GitHub.com 用了 17 个月,如何将 1200 台 MySQL 主机无缝升级到 8.0 版本?
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 范围内,其需要能够在不中断服务的情况下回滚到以前的 MySQL 5.7 版本。
- GitHub.com 的 MySQL 机群拥有非常多样化的工作负载。为了降低风险,工程师需要对每个数据库集群进行原子性升级(指的是一种将系统从一个版本或状态无缝地升级到另一个版本或状态的过程,该过程要么完全成功,要么完全失败。如果升级过程中发生故障或部分失败,系统可以回滚到之前的状态,以防止出现数据丢失或数据库处于不一致状态),并围绕其他重大变更安排升级时间。这意味着升级过程将是一个漫长的过程。因此,GitHub 工程团队从一开始就知道,需要有一个能够持续运行混合版本环境。
- 在该 8.0 副本的下游创建两个复制链:一组仅有 MySQL 5.7 版本副本(不提供流量,但可随时回滚)。一组只有 MySQL 8.0 副本(提供流量)。
- 在进入下一步之前,拓扑在这种状态下只维持了很短的时间(最多几个小时)。
- MySQL 8.0 引入了用于管理权限的角色,但在 MySQL 5.7 中不存在这一功能。当一个 8.0 实例提升为集群中的主实例时,我们遇到了问题。我们的配置管理正在扩展某些权限集,以包含角色语句并执行它们,这破坏了 5.7 副本中的下游复制。在升级窗口期间临时调整了受影响用户的已定义权限,从而解决了这个问题。
- 许多集群,当然还有所有最关键的集群,都长期处于高强度负载状态。GitHub 的大多数集群都是写入量非常大的集群。