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

GitHub数据库架构优化经验:MySql集群虚拟分区和零停机数据迁移

yuyutoo 2024-11-08 15:40 1 浏览 0 评论

熟悉Git线上仓库平台的同学都应该知道Githb和GitLab的基础机构都是一样,开始的时候都是以Ruby on Rails 为应用服务架构,以Mysql数据库为数据库架构。Gitlab经过版本迭代在Gitlab 9的时候数据库换成了PostgreSQL。Github由于是闭源只提供在线服务所以其架构演变我们对其知之甚少。最近GitHub随机数博客公布其数据库架构发展演变情况,请和虫虫一起来学习一下。

概述

GitHub于2007年开发,其初始公司叫Logical Awesome,三位创始人用Ruby on Rails共同开发。2008年2月beta版本上线,4月正式发布,7月发布代码片段收藏的Gists功能,12月发布网站托管的pages功能。2009年issues功能上线,基本功能完备。

其初始的数据架构为Mysql单实例,用来保存其底层元数据。多年来,其数据架构经历了多次迭代,用以支持不断增长的用户规模发展和功能需求。比如某些对某些功能数据进行横向分库,也通过主从副本以,用多个数据库分开来做负载均衡,并通过ProxySQL中间件做统一代理。

GitHub的核心数据架构为一个主要的数据库集群(称为 mysql1),其中包含GitHub核心功能服务的大部分数据,例如用户配置文件、存储库、问题和拉取请求等元数据。

2019年,为了应对面临的增长和可用性挑战,GitHub制定了一个数据架构优化计划,以改进其数据架构和关系数据库分区能力。

截止目前,该优化计划取得显著效果,其mysql1数据集群主机的负载减少了50%,极大地减少了与数据库相关的事件数量并提高了GitHub用户业务的可靠性。

虚拟分区

Github数据架构改进的第一个步是引了数据库模式的虚拟分区的概念。在物理移动数据库表之前,必须确保它们分离虚拟在应用程序层中 ,并且这必须在不影响开发新功能或现有功能的团队的情况下进行。为此,首先将统一在一起的数据库表分组到schema域中,并使用SQL linter强制执行域之间的边界。这样就可以在后续工作中可以安全地对数据进行分区,而不会以跨越分区的查询事务。

Schema域

schema域是用于实现虚拟分区的工具。Schema域描述了一组紧密耦合的数据库表,这些表在查询(例如,在使用表连接或子查询时)和事务中经常一起使用。 例如,gists域包含所有支持GitHub Gist功能的表——比如gists, gist_comments和starred_gists表。这些表是同属的,就应该一直在一起。Schema域是对其进行编码的第一步。

schema域设置了明确的边界,并暴露了功能之间有时隐藏的依赖关系。在Rails应用程序中,信息存储在一个简单的YAML配置文件中

db/schema-domains.yml. 其一个示例如下:

gists:

- gist_comments

- gists

- starred_gists

repositories:

- issues

- pull_requests

- repositories

users:

- avatars

- gpg_keys

- public_keys

- users

linter确保此文件中的表列表与我们的数据库模式相匹配。反过来,同一个 linter强制将schema域分配给每个表。

SQL linter

建立在schema域之上,两个新的SQL linter强制域之间的虚拟边界。他们通过添加查询注释并将它们视为豁免来识别跨越schema域的任何违规查询和事务。 如果一个域没有违规,它就被虚拟分区并准备好物理移动到另一个数据库集群。

查询 linter

查询linter 验证在同一数据库查询中只能引用属于同一schema域的表。如果它检测到来自不同域的表,它会抛出一个异常,并为开发人员提供一条有用的消息提示,以避免该问题。

由于linter仅在开发和测试环境中启用,因此开发人员在开发过程的早期就会遇到违规错误。此外,在CI运行期间,linter确保不会意外引入新的违规行为。

linter 有一种方法可以通过使用特殊注释SQL查询来抑制异常:

/* cross-schema-domain-query-exempted */

为了更加轻松翻边的添加注释,还构建了一个ActiveRecord方法, 以便更轻松:

Repository.joins(:owner).annotate("cross-schema-domain-query-exempted")

# => SELECT * FROM `repositories` INNER JOIN `users` ON `users`.`id` = `repositories.owner_id` /* cross-schema-domain-query-exempted */

通过注释所有导致失败的查询,可以构建需要修改的查询积压。常用来消除豁免的几种方法有:

有时,可以通过触发单独的查询而不是连接表来轻松解决豁免问题。 一个例子是使用 ActiveRecord的 preload方法而不是 includes.

另一个挑战是has_many:through导致的关系JOINs跨来自不同schema域的表。 为此,开发了一个通用的解决方案,has_many有一个 disable_joins告诉 Active Record 不要做任何事情的选项JOIN跨基础表的查询。相反,它会运行多个传递主键值的查询。

在应用程序中而不是在数据库中加入数据是另一种常见的解决方案。例如,替换 INNER JOIN带有两个单独查询的语句,而是在Ruby中执行“联合”操作(例如, A.pluck(:b_id) & B.where(id: ...))。

在某些情况下,这会带来惊人的性能 提升。根据数据结构和规模,MySQL 的查询计划器有时会创建次优的查询执行计划,而应用程序端连接则具有更稳定的性能成本。

与几乎所有与可靠性和性能相关的更改一样,将它们发布在这些 Scientist 实验之后,为请求的子集执行旧的和新的实现,使能够评估每个更改对性能的影响。

事务linter

除了查询,事务也是一个问题。现有的应用程序代码在编写时考虑了特定的数据库模式。MySQL事务保证数据库内表之间的一致性。如果事务包括对将移动到单独数据库的表的查询,它将不再能够保证一致性。

为了了解需要审查的事务,还引入了事务linter。与查询linter类似,它验证在给定事务中一起使用的所有表都属于同一schem域。

该linter在生产中运行并进行大量采样,以将性能影响降至最低。收集和分析 linting结果以了解大多数跨域事务发生的位置,使得可以决定更新某些代码路径或调整我们的数据模型。

在事务一致性保证至关重要的情况下,将数据提取到属于同一schema域的新表中。这确保它们保持在同一个数据库集群上,因此继续具有事务一致性。这通常发生在包含多态表来自不同schema域的数据的中(例如,一个reactions表存储不同功能的记录,如问题、拉取请求、讨论等)

零停机数据迁移

虚拟隔离schema域已准备好物理移动到另一个数据库集群。为了动态移动表,方案中使用了两种不同的方法:Vitess和自定义write-cutover过程。

Vitess

Vitess是云原生的MySQL代理中间件,可以用于对MySql集群进行分片和负载均衡,通过其垂直分片功能可以将生产中的多组表迁移到一起,而无需停机。

VTGate可以实时获取Vitess设置的当前状态,并通过另一个Vitess 组件VTTablet与MySQL实例对话。Vitess的表移动功能则由VReplication提供支持,负责数据库集群之间复制数据。

通过在K8S集群中部署Vitess VTGate,然后应用程序的数据池连接到VTGate进程,而无需直连接MySQL。Vitess MySQ协议,对后端应用是来说等同Mysq实例。

write-cutover过程

由于Vitess的采用在2020年初仍处于探索阶段,因此先开发了一种替代方法来一次性移动大量表格。这降低了依赖单一解决方案来确保GitHub持续可用的风险。

通过使用MySQL的常规复制功能将数据提供给另一个集群。最初,新集群被添加到旧集群的复制树中。然后脚本会快速执行一系列更改以实现切换。

在运行脚本之前,先准备应用程序和数据库复制,同一个复制的集群cluster_b是做为现有集群cluster_a的子集群. 用ProxySQL将客户端连接多路复用连接到 MySQL主数据库。cluster_b的ProxySQL配置为将流量路由到主cluster_a集群。ProxySQL使得能够快速更改数据库流量路由,并且对数据库客户端的影响最小。

通过这种设置,可以将数据库连接移动到cluster_b而无需改动任何东西。所有读取流量仍然流向从cluster_a基本的。所有写入流量都保留在cluster_a。

然后运行一个执行以下内容的转换脚本:

启用只读模式 cluster_a基本的。此时,所有写入cluster_a和cluster_b被阻止。所有尝试写入这些数据库主数据库的Web请求都失败并导致500报错。

从主集群cluster_a查看上次执行的MySQL GTID。

查询luster_b验证最后执行的GTID是否同步。

停止从cluster_a到cluster_b的主从复制。

更新 ProxySQL 路由配置将流量引导至cluster_b。

解除cluster_a和 cluster_b的只读模式。

经过充分的准备和练习,对于最繁忙的数据库表,通过这六个步骤可以在几十毫秒内实现数据切换。由于在一天中流量最低的时间执行此类转换,因此只会由于写入失败而导致少数面向用户的错误。该方法的结果比预期的要好。

经验教训

数据迁移过程中主要使用了write-cutover过程用于拆分mysql1集群。一次迁移了130个最繁忙的表及其支持GitHub的核心功能:存储库、问题和拉取请求。这个过程是作为一种风险缓解策略而创建的。

由于部署拓扑和读写支持等因素,实际中优化方案中并没有选择Vitess作为在每种情况下移动数据库表的工具。预计将来有机会将其用于大多数数据迁移。

结果

通过使用虚拟分区和多个inter检查器,GitHub数据库被划分成不同的Schema域实现不同业务的数据库横向分库。通过Vitess和自定义write-cutover过程实现具体分库的数据迁移,同时实现了数据架构的升级,升级后数据查询QPS由之前的95w/s到现在的120w/s,集群主机的平均负载也减少一半,性能上获得极大的提高,用户的响应和体验都得到极大改善。其数据架构升级过程和所用的工具也都是开源工具(比如ProxySQL,Vitess等),可供大家直接拿来直接使用。

相关推荐

jQuery VS AngularJS 你更钟爱哪个?

在这一次的Web开发教程中,我会尽力解答有关于jQuery和AngularJS的两个非常常见的问题,即jQuery和AngularJS之间的区别是什么?也就是说jQueryVSAngularJS?...

Jquery实时校验,指定长度的「负小数」,小数位未满末尾补0

在可以输入【负小数】的输入框获取到焦点时,移除千位分隔符,在输入数据时,实时校验输入内容是否正确,失去焦点后,添加千位分隔符格式化数字。同时小数位未满时末尾补0。HTML代码...

如何在pbootCMS前台调用自定义表单?pbootCMS自定义调用代码示例

要在pbootCMS前台调用自定义表单,您需要在后台创建表单并为其添加字段,然后在前台模板文件中添加相关代码,如提交按钮和表单验证代码。您还可以自定义表单数据的存储位置、添加文件上传字段、日期选择器、...

编程技巧:Jquery实时验证,指定长度的「负小数」

为了保障【负小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【负小数】的方法。HTML代码<inputtype="text"class="forc...

一篇文章带你用jquery mobile设计颜色拾取器

【一、项目背景】现实生活中,我们经常会遇到配色的问题,这个时候去百度一下RGB表。而RGB表只提供相对于的颜色的RGB值而没有可以验证的模块。我们可以通过jquerymobile去设计颜色的拾取器...

编程技巧:Jquery实时验证,指定长度的「正小数」

为了保障【正小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【正小数】的方法。HTML做成方法<inputtype="text"class="fo...

jquery.validate检查数组全部验证

问题:html中有多个name[],每个参数都要进行验证是否为空,这个时候直接用required:true话,不能全部验证,只要这个数组中有一个有值就可以通过的。解决方法使用addmethod...

Vue进阶(幺叁肆):npm查看包版本信息

第一种方式npmviewjqueryversions这种方式可以查看npm服务器上所有的...

layui中使用lay-verify进行条件校验

一、layui的校验很简单,主要有以下步骤:1.在form表单内加上class="layui-form"2.在提交按钮上加上lay-submit3.在想要校验的标签,加上lay-...

jQuery是什么?如何使用? jquery是什么功能组件

jQuery于2006年1月由JohnResig在BarCampNYC首次发布。它目前由TimmyWilson领导,并由一组开发人员维护。jQuery是一个JavaScript库,它简化了客户...

django框架的表单form的理解和用法-9

表单呈现...

jquery对上传文件的检测判断 jquery实现文件上传

总体思路:在前端使用jquery对上传文件做部分初步的判断,验证通过的文件利用ajaxFileUpload上传到服务器端,并将文件的存储路径保存到数据库。<asp:FileUploadI...

Nodejs之MEAN栈开发(四)-- form验证及图片上传

这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能。开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/R...

大数据开发基础之JAVA jquery 大数据java实战

上一篇我们讲解了JAVAscript的基础知识、特点及基本语法以及组成及基本用途,本期就给大家带来了JAVAweb的第二个知识点jquery,大数据开发基础之JAVAjquery,这是本篇文章的主要...

推荐四个开源的jQuery可视化表单设计器

jquery开源在线表单拖拉设计器formBuilder(推荐)jQueryformBuilder是一个开源的WEB在线html表单设计器,开发人员可以通过拖拉实现一个可视化的表单。支持表单常用控件...

取消回复欢迎 发表评论: