GitHub数据库架构优化经验:MySql集群虚拟分区和零停机数据迁移
yuyutoo 2024-11-08 15:40 4 浏览 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等),可供大家直接拿来直接使用。
相关推荐
- 全局和隐式 using 指令详解(全局命令)
-
1.什么是全局和隐式using?在.NET6及更高版本中,Microsoft引入了...
- 请停止微服务,做好单体的模块化才是王道:Spring Modulith介绍
-
1、介绍模块化单体是一种架构风格,代码是根据模块的概念构成的。对于许多组织而言,模块化单体可能是一个很好的选择。它有助于保持一定程度的独立性,这有助于我们在需要的时候轻松过渡到微服务架构。Spri...
- ASP.NET程序集引用之痛:版本冲突、依赖地狱等解析与实战
-
我是一位多年后端经验的工程师,其中前几年用ASP.NET...
- .NET AOT 详解(.net 6 aot)
-
简介AOT(Ahead-Of-TimeCompilation)是一种将代码直接编译为机器码的技术,与传统的...
- 一款基于Yii2开发的免费商城系统(一款基于yii2开发的免费商城系统是什么)
-
哈喽,我是老鱼,一名致力于在技术道路上的终身学习者、实践者、分享者!...
- asar归档解包(游戏arc文件解包)
-
要学习Electron逆向,首先要有一个Electron开发的程序的发布的包,这里就以其官方的electron-quick-start作为例子来进行一下逆向的过程。...
- 在PyCharm 中免费集成Amazon CodeWhisperer
-
CodeWhisperer是Amazon发布的一款免费的AI编程辅助小工具,可在你的集成开发环境(IDE)中生成实时单行或全函数代码建议,帮助你快速构建软件。简单来说,AmazonCodeWhi...
- 2014年最优秀JavaScript编辑器大盘点
-
1.WebstormWebStorm是一种轻量级的、功能强大的IDE,为Node.js复杂的客户端开发和服务器端开发提供完美的解决方案。WebStorm的智能代码编辑器支持JavaScript,...
- 基于springboot、tio、oauth2.0前端vuede 超轻量级聊天软件分享
-
项目简介:基于JS的超轻量级聊天软件。前端:vue、iview、electron实现的PC桌面版聊天程序,主要适用于私有云项目内部聊天,企业内部管理通讯等功能,主要通讯协议websocket。支持...
- JetBrains Toolbox推出全新产品订阅授权模式
-
捷克知名软件开发公司JetBrains最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境IntelliJIDEA,相信很多开发者都有所了解。而近期自2015年11月2日起,JetBr...
- idea最新激活jetbrains-agent.jar包,亲测有效
-
这里分享一个2019.3.3版本的jetbrains-agent.jar,亲测有效,在网上找了很多都不能使用,终于找到一个可以使用的了,这里分享一下具体激活步骤,此方法适用于Jebrains家所有产品...
- CountDownTimer的理解(countdowntomars)
-
CountDownTimer是android开发常用的计时类,按照注释中的说明使用方法如下:kotlin:object:CountDownTimer(30000,1000){...
- 反射为什么性能会很慢?(反射时为什么会越来越长)
-
1.背景前段时间维护一个5、6年前的项目,项目总是在某些功能使用上不尽人意,性能上总是差一些,仔细过了一下代码发现使用了不少封装好的工具类,工具类里面用了好多的反射,反射会影响到执行效率吗?盲猜了一...
- btrace 开源!基于 Systrace 高性能 Trace 工具
-
介绍btrace(又名RheaTrace)是抖音基础技术团队自研的一款高性能AndroidTrace工具,它基于Systrace实现,并针对Systrace不足之处加以改进,核心改进...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- .NET 奇葩问题调试经历之3——使用了grpc通讯类库后,内存一直增长......
- 全局和隐式 using 指令详解(全局命令)
- 请停止微服务,做好单体的模块化才是王道:Spring Modulith介绍
- ASP.NET程序集引用之痛:版本冲突、依赖地狱等解析与实战
- .NET AOT 详解(.net 6 aot)
- 一款基于Yii2开发的免费商城系统(一款基于yii2开发的免费商城系统是什么)
- asar归档解包(游戏arc文件解包)
- 在PyCharm 中免费集成Amazon CodeWhisperer
- 2014年最优秀JavaScript编辑器大盘点
- 基于springboot、tio、oauth2.0前端vuede 超轻量级聊天软件分享
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)