当前位置: 首页 > 科技观察

从MySQL5.6升级到8.0让我们付出了巨大的代价!_0

时间:2023-03-14 12:51:30 科技观察

图片来自宝途网在升级到5.7的同时构建新的存储引擎会大大拖慢MyRocks的进度,所以我们选择继续使用5.6直到MyRocks完成,MySQL5.6的生命周期即将到来结束,所以我们决定升级到MySQL8.0。据官方博客介绍,这个过程比之前的升级更具挑战性。MySQL是甲骨文公司拥有的开源数据库,为Facebook的一些最重要的工作负载提供支持。我们积极开发MySQL的新功能以支持不断变化的需求。这些功能修改了MySQL的许多方面,包括客户端连接器、存储引擎、优化器和复制。为了迁移工作负载,我们需要在每个新的MySQL主要版本上投入大量时间和精力。其中的挑战包括:将自定义功能移植到新版本。确保主要版本之间的复制兼容性。最小化现有应用程序查询所需的更改。修复了阻止服务器支持我们的工作负载的性能回归。我们上一次的主要版本升级是MySQL5.6,它花了一年多的时间才推出。当5.7版发布时,我们还在5.6版上开发LSM树存储引擎和MyRocks。在升级到5.7的同时构建新的存储引擎会显着减慢MyRocks的进度,因此我们选择留在5.6上,直到MyRocks完成。随着MySQL8.0的发布,我们将MyRocks部署到用户数据库(UDB)服务层。此版本包括引人注目的功能,例如基于写入集的并行复制和支持原子DDL的事务数据字典。对我们来说,迁移到8.0还将带来我们错过的5.7功能,包括文档存储。5.6版本即将结束其使命,我们希望在MySQL社区中保持活跃,尤其是我们在MyRocks存储引擎方面的工作。8.0中的增强功能(例如即时DDL)可以加快MyRocks的模式更改,但我们在8.0代码库中需要它。考虑到更新代码的好处,我们决定迁移到8.0。下面将分享我们是如何解决8.0迁移项目的难点,以及过程中发现的一些惊喜。最初确定项目范围时,很明显迁移到8.0比迁移到5.6或MyRocks更困难:当时,我们的自定义5.6分支有超过1700个代码补丁需要移植到8.0。在我们移植这些更改的同时,新的FacebookMySQL功能和修复已添加到5.6代码库中,使目标更加遥不可及。我们在生产环境中运行着许多MySQL服务器,为大量不同的应用程序提供服务。我们还有许多用于管理MySQL实例的软件架构。这些应用程序执行收集统计信息或管理服务器备份等操作。从5.6升级到8.0会完全跳过5.7。在5.6中活跃的API可能会在5.7中被弃用,并可能在8.0中被删除,这要求我们更新所有使用现已删除的API的应用程序。许多Facebook功能与8.0中的类似功能不向前兼容,需要弃用或迁移路径。要在8.0中运行,需要对MyRocks进行增强,包括本地化分区和崩溃恢复。代码补丁首先我们在开发环境中建立了8.0分支用于构建和测试。然后我们开始了从5.6分支移植补丁的漫长过程。我们从1700多个补丁开始,但我们能够将它们组织成几个主要类别。我们的大部分自定义代码都有很好的注释和描述,因此很容易确定应用程序是否仍然需要它,或者是否可以将其删除。由特殊关键字或唯一变量名启用的功能也可以轻松确定关联,因为我们可以搜索应用程序代码库以找到它们的用例。有些补丁非常模糊,需要进行调查工作——挖掘旧的设计文档、电子邮件或代码审查评论以了解它们的历史。我们将每个补丁分为四个类别之一:丢弃:已弃用,或8.0中的等效功能,不需要移植。Build/Client:支持我们构建环境的非服务器特性,需要移植修改MySQL工具,如mysqlbinlog,或新增功能,如异步客户端API等。Non-MyRocks服务器:mysqld服务器中与MyRocks存储引擎无关,需要移植的特性。MyRocks服务器:支持MyRocks存储引擎的特性,需要移植。我们使用电子表格来跟踪每个补丁的状态和历史记录,并在删除补丁时记录理由。更新相同功能的多个补丁被组合在一起进行移植。补丁移植并提交到8.0分支,用5.6提交消息注释。由于我们需要筛选的补丁较多,移植状态难免会有差异,这些评论有助于我们解决此类问题。客户端和服务器类别中的每个补丁本身就是一个软件发布里程碑。移植所有与客户端相关的更改后,我们能够将客户端工具和连接器代码更新到8.0。一旦移植了所有非MyRocks服务器功能,我们就可以为InnoDB服务器部署8.0mysqld。完成MyRocks服务器功能端口允许我们更新MyRocks安装。一些复杂的功能需要对8.0进行重大更改,并且某些区域存在重大兼容性问题。比如上游8.0的binlogevent格式就和我们5.6的一些自定义修改不兼容。Facebook5.6功能使用的错误代码与上游8.0分配给新功能的错误代码冲突。我们最终需要修补5.6服务器,使其向前兼容8.0。移植所有这些功能花了几年时间。到当天结束时,我们已经评估了2300多个补丁,并将其中的1500个补丁移植到了8.0版本。迁移路径我们将多个mysqld实例合并到一个MySQL副本集中。副本集中的每个实例都包含相同的数据,但在地理上分布在不同的数据中心,以提供数据可用性和故障转移支持。每个副本集都有一个主实例。其余实例是从属实例。主实例处理所有写入流量并将数据异步复制到所有从实例。从包含5.6个主节点/5.6个从节点的副本集开始,最终目标是包含8.0个主节点/8.0个从节点的副本集。我们遵循类似于UDBMyRocks迁移计划的迁移计划:对于每个副本集,从使用mysqldump生成的逻辑备份创建并添加到8.0从属实例。这些从属实例不提供任何应用程序读取流量。在8.0从属实例上启用读取流量。允许将8.0从实例升级为主实例。禁用5.6实例的读取流量。删除所有5.6实例。每个副本集都可以通过上述步骤独立迁移,并根据需要停留在一个步骤上。我们将副本集分成更小的组,并在组内进行每次迁移。如果发现问题,我们可以回滚到上一步。在某些情况下,副本集可以在其他副本集开始之前到达最后一步。为了自动迁移大型副本集,我们需要构建一个新的软件架构。只需更改配置文件中的一行,即可组合副本集并将它们移动到每个阶段。任何遇到问题的副本集都可以单独回滚。基于行的复制作为8.0迁移工作的一部分,我们决定使用基于行的复制(RBR)作为标准。一些8.0功能需要RBR,它简化了MyRocks的移植。我们的大多数MySQL副本集已经在使用RBR,那些仍在运行基于语句的复制(SBR)的副本集不容易迁移。这些副本集通常包含没有任何高基数键的表。完全转向RBR是一个目标,但添加主键所需的长尾工作往往比其他项目的优先级低。因此,我们将RBR作为8.0的要求。在对每个表进行评估和添加主键之后,我们切换了今年最后一个SBR副本集。使用RBR还为我们在将一些副本集移动到8.0主节点时遇到的应用程序问题提供了替代解决方案,稍后将讨论。自动验证大多数8.0迁移过程涉及使用我们的自动模式和应用程序查询来测试和验证mysqld服务器。我们用来管理服务器的自动化基础设施随着MySQL服务器的增长而增长。为确保所有MySQL自动化组件都与8.0版兼容,我们投资了一个测试环境,该环境利用虚拟机上的测试副本集来验证行为。我们为在5.6和8.0版本上运行的每个自动化组件编写了金丝雀集成测试,并验证了它们的正确性。在执行此演练时,我们发现了一些错误和行为差异。虽然MySQL体系结构的每个部分都在我们的8.0服务器上得到了验证,但我们发现并修复了(或解决了)一些有趣的问题:解析错误日志、mysqldump输出或服务器show命令的文本输出的软件很容易破坏.服务器输出的细微变化通常会暴露工具解析逻辑中的错误。8.0的默认utf8mb4归类设置导致5.6和8.0实例之间的归类不匹配。8.0表可以使用新的utf8mb4_0900排序规则,即使对于5.6的showcreatetable生成的创建语句也是如此,因为使用utf8mb4_general_ci的5.6模式没有明确指定排序规则。这些表差异通常会导致复制和模式验证工具出现问题;某些复制失败的错误代码发生了变化,我们必须修复我们的自动化以正确处理它们。数据字典8.0版弃用了table.frm文件,但我们的一些自动化系统使用它们来检测表架构修改。我们必须更新自动化系统以支持8.0中引入的动态权限。应用程序验证我们希望迁移对应用程序尽可能透明,但某些应用程序查询会遇到性能下降或在8.0上失败。对于MyRocks迁移,我们构建了一个MySQL影子测试框架,用于捕获生产流量并将其重播到测试实例中。对于每个应用程序工作负载,我们在8.0上创建了测试实例,并向它们回放影子流量的查询。我们捕获并记录了从8.0服务器返回的错误,并发现了一些有趣的问题。不幸的是,并非所有这些问题都在测试期间被发现。例如,事务死锁是在迁移期间由应用程序发现的。在研究不同的解决方案时,我们可以暂时将这些应用程序回滚到5.6版。8.0引入了新的保留关键字,其中一些关键字(如groups和rank)与应用程序查询中常用的表列名称或别名冲突。这些查询不使用反引号转义名称,从而导致解析错误。使用自动转义查询中列出的软件库的应用程序不会遇到这些问题,但并非所有应用程序都使用这些软件库。解决这个问题很简单,但需要时间来追踪生成这些查询的应用程序所有者和代码库。在5.6和8.0之间还发现了一些REGEXP不兼容问题。一些涉及insert...onduplicatekey查询的InnoDB应用程序遇到了可重复读取的事务死锁。5.6有一个bug,8.0修复了,但是修复增加了事务死锁的可能性。分析查询后,我们能够通过降低隔离级别来解决问题。我们可以使用此选项,因为我们已切换到基于行的复制。我们的自定义5.6文档存储和JSON函数与8.0不兼容。使用文档存储的应用程序需要将文档类型转换为文本以进行迁移。对于JSON函数,我们在8.0服务器中添加了一个5.6兼容版本,以便将来应用程序可以迁移到8.0API。我们对8.0服务器的查询和性能测试揭示了一些需要立即解决的问题:我们在ACL缓存部分发现了一个新的互斥竞争热点。当同时打开大量连接时,它们都会阻塞ACL检查。在binlog文件数量多,binlog写入速度快导致文件轮转频繁的情况下,也发现了binlog索引访问的类似争用。涉及临时表的几个查询被中断。这些查询会返回意外错误,或者运行时间过长以致超时。与5.6相比,内存使用有所增加,特别是对于MyRocks实例,因为必须加载8.0中的InnoDB。默认的performance_schema设置启用了所有工具集并消耗大量内存。我们限制了内存使用,仅启用了少量工具,并更改了代码以禁用无法手动关闭的表。但是,并非所有增加的内存都分配给performance_schema。我们需要检查和修改各种InnoDB内部数据结构,以进一步减少内存占用。这一努力使8.0的内存使用率降低到可接受的水平。下一步是什么到目前为止,8.0移植已经花费了数年时间。我们已经将许多InnoDB副本集转换为完全在8.0上运行。其余大部分处于迁移路径的不同阶段。现在我们的大部分自定义功能已经移植到8.0,更新到Oracle的次要版本相对容易,我们计划跟上最新版本。跳过像5.7这样的主要版本会引入我们的迁移需要解决的问题。首先,我们无法就地升级服务器,我们需要使用逻辑转储和恢复来构建新服务器。然而,对于非常大的mysqld实例,这可能需要很多天才能在活动的生产服务器上运行,并且这个脆弱的过程可能会在完成之前被中断。对于这些大型实例,我们不得不修改备份和恢复系统来处理重建。其次,检测API更改要困难得多,因为5.7可能会向我们的应用程序客户端发出弃用警告,作为修复潜在问题的提示。相反,我们需要在迁移生产工作负载之前运行额外的影子测试来发现故障。使用自动转义模式对象名称的mysql客户端软件,有助于减少兼容性问题的数量。在副本集中支持两个主版本非常困难。一旦副本集将其主实例升级到8.0,最好尽快禁用并删除5.6实例。应用程序用户通常会发现仅在8.0中支持的新功能,例如utf8mb4_0900排序规则,使用它可能会中断8.0和5.6实例之间的复制流。尽管我们在迁移过程中遇到了所有障碍,但我们已经看到了运行8.0的好处。一些应用程序选择提前迁移到8.0以利用文档存储和改进的日期时间支持等功能。我们一直在考虑如何在MyRocks上支持存储引擎功能,例如即时DDL。总体而言,新版本大大扩展了MySQL@Facebook的功能。作者:HermanLee,PradeepNayak译者:王雪莹编辑:陶家龙来源:转载自公众号CSDN(ID:CSDNnews)链接:https://engineering.fb.com/2021/07/22/data-infrastructure/MySQL/