GitHub数据库架构优化经验:MySql集群虚拟分区和零停机数据迁移
yuyutoo 2024-11-08 15:40 2 浏览 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等),可供大家直接拿来直接使用。
相关推荐
- 史上最全的浏览器兼容性问题和解决方案
-
微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...
-
- 平面设计基础知识_平面设计基础知识实验收获与总结
-
CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...
-
2025-02-21 16:01 yuyutoo
- 写作排版简单三步就行-工具篇_作文排版模板
-
和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...
- 写一个2048的游戏_2048小游戏功能实现
-
1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...
- 今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……
-
最近的天气暖和得让很多小伙伴们喊“热”!!! 昨天的气温到底升得有多高呢?你家有没有榜上有名?...
- CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式
-
之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...
- 你的自我界限够强大吗?_你的自我界限够强大吗英文
-
我的结果:A、该设立新的界限...
- 行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?
-
行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...
-
- 让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
-
去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...
-
2025-02-21 16:00 yuyutoo
- 今年国家综合性消防救援队伍计划招录消防员15000名
-
记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...
- 一起盘点最新 Chrome v133 的5大主流特性 ?
-
1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...
- 竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使
-
style="text-align:center;"data-mce-style="text-align:...
- 学物理能做什么?_学物理能做什么 卢昌海
-
作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...
-
- 你不知道的关于这只眯眼兔的6个小秘密
-
在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...
-
2025-02-21 16:00 yuyutoo
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 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)