本文概述Vitess架构,如需了解更多信息,请在文章底部查看参考资料。本文基于笔者研究,可能与具体实践情况有所差异。
从前,三个PayPal前员工打算创建一个约会网站,但这个商业点子没成功。于是他们转而做了一个视频分享网站,并将其命名为YouTube。
最初,他们将视频标题、描述以及用户数据存储在 MySQL 中。随着用户数量快速增长,为了扩展读取能力,他们采用了经典的主从(Leader-Follower)复制拓扑结构。

MySQL 主从复制拓扑
然而,MySQL的复制过程是单线程的。当主库写入压力极大时,从库可能无法跟上主库的数据同步速度,导致数据延迟。但YouTube的增长是爆炸式的,它迅速成为全球第二大访问量网站,拥有惊人的用户量。

作为应对,团队通过增加缓存来横向扩展,并预先将MySQL二进制日志中的所有事件加载到内存中,这使得复制过程从I/O限制转变为内存限制,速度变得更快。
虽然这一招暂时缓解了可扩展性压力,但新的挑战又出现了:
1. 分片
为了应对海量的存储需求,必须对 MySQL 进行分区,也就是分片。但分片之后,跨片的事务和连接变得异常困难,处理逻辑不得不交给应用层。这意味着,应用逻辑需要先找到目标分片才能查询,这不仅增加了代码复杂度,也提高了系统出错和停机的风险。
2. 性能
在主从复制架构下,从从库读取的数据可能是过时的。如果业务逻辑要求读取实时(新鲜)数据,应用就必须将读请求路由到主库。这同样需要在应用层实现额外的、复杂的路由逻辑。
3. 保护
某些查询可能会因为执行时间过长而阻塞资源,同时,过多的MySQL连接本身也是一个严重问题,有可能直接压垮数据库实例。
他们希望在 MySQL 之上构建一个抽象层,以同时实现简单性和极致的可扩展性。于是,Vitess诞生了。
以下是Vitess提供大规模可扩展能力的关键方式:
1. 与数据库交互
他们在每个MySQL实例前都部署了一个“边车”(sidecar)代理服务器,称之为VTTablet。

VTTablet作为边车服务器运行
VTTablet的主要作用包括:
- 控制
MySQL服务器并管理数据库备份。
- 通过添加
LIMIT等子句,重写那些可能代价高昂的查询。
- 缓存频繁访问的数据,防止“惊群效应”(多个请求同时冲击数据库)。
2. 路由SQL查询
他们设置了一个无状态的代理服务器来负责查询路由,称之为VTGate。

VTGate将查询路由到特定分片
VTGate扮演了核心路由器的角色:
- 根据数据模式和分片方案,找到正确的VTTablet来执行查询。
- 通过连接池机制,维持与底层
MySQL的少量、高效连接,避免连接数过多的问题。
- 对应用层,它使用标准的
MySQL协议通信,应用可以像连接一个单体的MySQL服务器一样连接它,简化了开发。
- 限制并发事务的数量以提升整体性能。

使用多个VTGate服务器进行扩展
通过运行多个VTGate实例,可以轻松实现水平扩展,以应对海量的前端请求。
3. 状态信息
他们需要一个地方来集中存储整个集群的元数据,例如表结构、分片规则以及各个数据库的角色(主库/从库)。为此,他们引入了一个分布式的键值数据库。

存储元信息的键值数据库
这个键值数据库管理着所有数据库之间的关系。YouTube最初选用ZooKeeper来实现这个键值存储。为了提高性能,VTGate会将这些元信息缓存起来。

更新键值数据库
他们运行了一个名为VTctld的HTTP服务器,负责维护键值数据库中的信息。VTctld会收集所有数据库服务器及其拓扑关系的完整列表,并据此更新键值存储。
总结

Vitess的高级架构
至此,Vitess的核心架构已经清晰:
- VTGate:无状态代理,负责SQL查询路由,是应用的统一入口。
- 键值数据库:存储集群拓扑和配置信息的配置服务器。
- VTTablet:部署在每个
MySQL实例前的边车代理,负责直接与数据库交互并提供保护、优化能力。
YouTube的工程师们选择使用 Go 语言编写了Vitess并将其开源。现在,它也同样支持MariaDB。
通过Vitess与MySQL的组合,YouTube成功支撑了24.9亿用户。这个经典的案例表明,通过合理的架构设计,MySQL完全有能力处理互联网级别的巨大流量。这个解决高并发与分片管理难题的方案,对于面临类似挑战的开发者而言,具有极高的参考价值,相关讨论也可以在技术社区如云栈社区的后端架构板块找到。
参考资料
来源:newsletter.systemdesign.one/p/vitess-mysql?utm_source=profile&utm_medium=reader2