首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >绝对干货!Facebook迁移MySQL 8.0的全过程详解!

绝对干货!Facebook迁移MySQL 8.0的全过程详解!

原创
作者头像
JavaEdge
发布2024-12-26 13:19:23
发布2024-12-26 13:19:23
4630
举报

0 前言

涉及 MySQL 的多个领域,包括客户端连接器、存储引擎、优化器和复制。每个新版本 MySQL 都需要我们投入大量时间和精力来迁移工作负载,主要挑战:

  • 将定制功能移植到新版本;
  • 确保主要版本之间的复制功能兼容;
  • 最小化现有应用程序查询所需的更改;
  • 修复性能回退问题,以确保服务器能够支持我们的工作负载。

上次重大版本升级到 MySQL 5.6 花费一年多。当 5.7 发布时,我们还在基于 5.6 开发 LSM-Tree 存储引擎 MyRocks。因为在开发新存储引擎的同时升级到 5.7 会显著拖慢 MyRocks 的进展,我们选择在完成 MyRocks 之前继续使用 5.6。当我们将 MyRocks 部署到用户数据库(UDB)服务层的过程接近尾声时,MySQL 8.0 发布了。

此版本带来了一些重要功能,如基于写集合的并行复制和支持事务性数据字典的原子 DDL。迁移到 8.0 还将让我们获得之前未升级到 5.7 时错过的功能,比如 Document Store。随着 5.6 临近生命周期终点,我们希望继续在 MySQL 社区中保持活跃,尤其是与 MyRocks 存储引擎相关的工作。8.0 的改进功能(如即时 DDL)能够加快 MyRocks 架构变更,但我们需要迁移到 8.0 代码库才能使用这些功能。综合考虑后,我们决定迁移到 8.0。在此,我们分享了如何完成 8.0 的迁移项目,以及过程中发现的一些意外挑战。在最初评估项目时,我们发现迁移到 8.0 的难度甚至超过了之前的迁移工作,如迁移到 5.6 或开发 MyRocks。

  • 当时,我们定制的 5.6 分支包含超过 1,700 个需要移植到 8.0 的代码补丁。在移植这些更改的过程中,Facebook MySQL 的新功能和修复不断被添加到 5.6 代码库中,使得迁移难度进一步增加。
  • 我们的生产环境运行着许多 MySQL 服务器,为各种应用程序提供支持,同时还有广泛的软件基础设施来管理这些实例。这些应用包括统计信息收集和备份管理。
  • 从 5.6 升级到 8.0 跳过了 5.7。这意味着 5.6 中一些活跃的 API 可能在 5.7 中被弃用,并在 8.0 中移除,需要对使用这些 API 的应用程序进行更新。
  • 一些 Facebook 功能与 8.0 中的类似功能不兼容,需要设计相应的弃用和迁移路径。
  • 为了在 8.0 中运行 MyRocks,需要对其进行增强,如支持本地分区和崩溃恢复。

1 代码补丁

我们首先在开发环境中搭建了 8.0 分支,用于构建和测试。随后,我们开始了将 5.6 分支的补丁移植到 8.0 的漫长工作。起初有超过 1,700 个补丁需要移植,但我们将其归为几个主要类别。大部分自定义代码都有详细的注释和说明,这使得我们可以轻松判断这些功能是否仍被应用程序需要,或是否可以移除。通过特殊关键字或唯一变量名启用的功能也很容易通过在应用代码库中进行搜索来确定其相关性。一些补丁非常晦涩,需要我们深入挖掘,例如查阅旧的设计文档、帖子以及代码审查记录,以了解其历史背景。

我们将补丁分为以下四类:

  1. 移除: 不再使用或在 8.0 中已有等效功能的补丁无需移植。
  2. 构建/客户端: 用于支持构建环境的非服务器功能,以及修改 MySQL 工具(如 mysqlbinlog)或新增功能(如异步客户端 API)的补丁需要移植。
  3. 非 MyRocks 服务器: 与 MyRocks 存储引擎无关的 mysqld 服务器功能需要移植。
  4. MyRocks 服务器: 专用于 MyRocks 存储引擎的功能需要移植。

我们使用电子表格记录每个补丁的状态及相关历史信息,并在移除补丁时记录具体理由。多个补丁若涉及相同功能,会分组处理后再移植。移植并提交到 8.0 分支的补丁会标注对应的 5.6 提交信息。由于需要处理的大量补丁,移植状态偶尔会出现不一致,但这些记录帮助我们解决了这些问题。

客户端和服务器类别的每一部分功能自然形成了一个软件发布里程碑。完成所有客户端相关更改后,我们成功将客户端工具和连接器代码更新至 8.0。一旦非 MyRocks 服务器功能移植完成,我们便可部署 8.0 mysqld 用于 InnoDB 服务器。最后,完成 MyRocks 服务器功能的移植后,我们便可更新 MyRocks 安装。

一些最复杂的功能在 8.0 中需要进行重大更改,部分领域甚至存在严重的兼容性问题。例如,上游 8.0 的 binlog 事件格式与我们自定义的 5.6 修改存在不兼容。Facebook 5.6 功能中使用的错误代码与上游 8.0 中为新功能分配的错误代码发生冲突。最终,我们需要修补 5.6 服务器以实现对 8.0 的前向兼容。

完成所有功能移植耗时两年。在此期间,我们评估了超过 2,300 个补丁,并最终将其中的 1,500 个移植到了 8.0。

2 迁移路径

将多个 mysqld 实例分组为一个 MySQL 副本集。每个副本集的实例包含相同的数据,但分布在不同的地理数据中心,以确保数据的可用性并支持故障转移。每个副本集中有一个主实例,其余为从实例。主实例处理所有写入操作,并将数据以异步方式复制到所有从实例。

目标是将当前由 5.6 主实例和 5.6 从实例组成的副本集迁移为由 8.0 主实例和 8.0 从实例组成的副本集。采取类似 UDB MyRocks 迁移计划的方法:

  1. 对每个副本集,通过 mysqldump 逻辑复制创建并添加 8.0 从实例。这些从实例不会承载应用程序读取流量
  2. 启用 8.0 从实例的读取流量
  3. 将 8.0 实例提升为主实例
  4. 禁用 5.6 实例的读取流量
  5. 移除所有 5.6 实例

每个副本集可以独立按照上述步骤迁移,并且可以在某一步停留足够的时间。我们将副本集划分为更小的组,逐步完成迁移。遇到问题时,可以回滚到上一阶段。在某些情况下,一些副本集能够在其他副本集开始迁移之前完成所有步骤。

为了自动化大量副本集的迁移,我们开发了新的软件基础设施。通过简单修改配置文件中的一行内容,我们即可将多个副本集分组,并统一迁移到下一阶段。对于出现问题的副本集,我们可以单独回滚。

2.1 基于行的复制

在 8.0 的迁移过程中,我们决定统一采用基于行的复制(RBR)。部分 8.0 功能要求使用 RBR,同时这也简化了我们 MyRocks 的移植工作。尽管我们的大部分 MySQL 副本集已经在使用 RBR,但仍有一些副本集依然采用基于语句的复制(SBR),这些副本集通常包含没有高基数键的表。完全切换到 RBR 一直是我们的目标,但为每张表添加主键的工作优先级通常低于其他项目。

因此,我们将 RBR 设为 8.0 的必备条件。经过评估并为每张表添加主键后,我们在今年完成了最后一个 SBR 副本集的切换。RBR 的使用还为我们解决了一些应用问题提供了替代方案,例如部分副本集在迁移到 8.0 主实例时遇到的应用兼容性问题,这将在后续章节中详细讨论。

3 自动化验证

在 8.0 的迁移过程中,大部分工作集中于通过我们的自动化基础设施和应用程序查询来测试和验证 mysqld 服务器。

随着 MySQL 集群规模的扩大,我们管理服务器的自动化基础设施也在不断扩展。为确保所有 MySQL 自动化流程与 8.0 版本兼容,我们投入资源构建了一个测试环境,该环境使用虚拟机上的测试复制集来验证行为。我们开发了集成测试,以检查每个自动化组件是否能够同时在 5.6 和 8.0 上正确运行。在此过程中,我们发现并修复了多个漏洞和行为差异。

在验证 8.0 服务器的自动化基础设施时,我们遇到了以下问题并进行了相应的解决或调整:

  1. 解析错误日志、mysqldump 输出或服务器 show 命令文本输出的软件容易出错。服务器输出的微小变化常常暴露工具解析逻辑中的缺陷。
  2. 8.0 默认的 utf8mb4 排序规则设置导致了 5.6 和 8.0 实例之间的排序规则不匹配问题。例如,5.6 的 show create table 命令生成的语句可能在 8.0 表中使用新的 utf8mb4_0900 排序规则,而不是 5.6 的 utf8mb4_general_ci。这种表结构的差异容易引发复制和架构验证工具的问题。
  3. 某些复制失败的错误代码发生变化,我们更新了自动化以正确处理这些错误。
  4. 8.0 数据字典取代了表的 .frm 文件,但我们的部分自动化系统使用这些文件检测表架构的变化。
  5. 我们更新了自动化以支持 8.0 引入的动态权限。

3.1 应用程序验证

我们希望迁移对应用程序尽可能透明,但某些应用程序查询在 8.0 上遇到了性能回退或错误。

在 MyRocks 迁移过程中,我们构建了一个 MySQL 影子测试框架,用于捕获生产流量并将其重放到测试实例上。对于每个应用工作负载,我们在 8.0 上搭建了测试实例,并将影子流量查询重放到这些实例中。我们捕获并记录了 8.0 服务器返回的错误,并发现了一些有趣的问题。不幸的是,并非所有问题都能在测试中被发现。例如,事务死锁问题是在迁移过程中由应用程序发现的。我们临时将这些应用程序回滚到 5.6,同时研究解决方案。

  • 8.0 引入的一些新保留关键字(如 groups 和 rank)与应用程序查询中流行的表列名或别名冲突。这些查询未通过反引号转义名称,导致解析错误。使用自动转义列名的库的应用程序未受到影响,但并非所有应用程序都使用了这些库。问题虽然容易修复,但定位生成这些查询的代码库和应用所有者花费了一些时间。
  • 我们还发现了 5.6 和 8.0 之间的某些 REGEXP 不兼容问题。
  • 一些应用程序在 InnoDB 上执行 insert ... on duplicate key 查询时,遇到了可重复读事务死锁。5.6 中的一个 Bug 在 8.0 得到修复,但修复增加了事务死锁的可能性。通过分析查询后,我们通过降低隔离级别解决了此问题。由于我们已经切换到基于行的复制,这一选择得以实现。
  • 我们定制的 5.6 Document Store 和 JSON 函数与 8.0 不兼容。使用 Document Store 的应用程序需要将文档类型转换为文本以完成迁移。对于 JSON 函数,我们在 8.0 服务器中添加了 5.6 兼容版本,使应用程序能够在稍后迁移到 8.0 API。

4 查询与性能问题

在对 8.0 服务器进行查询和性能测试时,我们发现了一些需要紧急解决的问题:

  • 我们发现 ACL 缓存的某些区域出现了新的互斥锁竞争热点。当大量连接同时建立时,这些连接可能在检查 ACL 时发生阻塞。
  • 在 binlog 索引访问中也存在类似的竞争问题,尤其是在 binlog 文件数量较多且 binlog 写入速率较高,导致文件频繁轮换的情况下。
  • 涉及临时表的某些查询无法正常运行。这些查询可能返回意外的错误,或者运行时间过长,最终超时。

相比于 5.6,8.0 的内存使用量显著增加,尤其是在运行 MyRocks 的实例上,因为 8.0 要求必须加载 InnoDB。此外,默认启用的 performance_schema 设置打开了所有监控工具,占用了大量内存。我们通过仅启用少量必要工具,并对代码进行修改以禁用无法手动关闭的表,来限制 memory_schema 的内存使用。然而,并非所有内存增加都由 performance_schema 导致。我们需要检查并优化 InnoDB 的内部数据结构,以进一步减少内存占用。这些调整最终将 8.0 的内存使用控制在了可以接受的范围内。

5 下一步

8.0 的迁移已经进行了数年时间。我们已将许多 InnoDB 复制集完全迁移至 8.0,剩余的复制集则处于迁移路径的不同阶段。随着我们的大部分自定义功能已移植到 8.0,更新至 Oracle 发布的小版本变得更加轻松,我们计划持续跟随最新版本的步伐。

跳过像 5.7 这样的大版本升级引发了一些问题,这些问题在我们的迁移过程中需要解决。

首先,我们无法直接在现有服务器上进行升级,只能通过逻辑导出和恢复来创建新服务器。然而,对于非常大的 mysqld 实例,这个过程可能需要数天时间才能完成,并且由于运行在生产服务器上的迁移过程十分脆弱,常常在完成之前被中断。针对这些大规模实例,我们不得不修改备份和恢复系统以支持重建。

其次,由于没有经历 5.7 阶段,我们难以提前发现 API 的更改。5.7 原本可以通过弃用警告帮助我们修复潜在问题,而我们不得不运行更多影子测试来预先捕获可能的故障。采用能自动转义模式对象名称的 mysql 客户端软件,有助于减少兼容性问题。

同时,在一个复制集中同时支持两个主要版本十分困难。一旦复制集的主实例被提升为 8.0,我们建议尽快禁用并移除 5.6 实例。因为应用用户可能会开始使用仅由 8.0 支持的新功能(如 utf8mb4_0900 排序规则),从而导致 8.0 和 5.6 实例之间的复制流断开。

尽管迁移充满挑战,我们已经看到了运行 8.0 带来的显著好处。一些应用程序甚至选择提前迁移至 8.0,以利用新版本的功能,例如 Document Store 和改进的日期时间支持。同时,我们也在探索如何支持 MyRocks 的存储引擎功能(如即时 DDL)。总体而言,新版本大大扩展了我们在 Facebook 使用 MySQL 的能力。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 前言
  • 1 代码补丁
  • 2 迁移路径
    • 2.1 基于行的复制
  • 3 自动化验证
    • 3.1 应用程序验证
  • 4 查询与性能问题
  • 5 下一步
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档