高性能 MySQL 第四版(GPT 重译)(四)(1)https://developer.aliyun.com/article/1484355
多个分区键
复杂的数据模型使数据分片更加困难。许多应用程序有多个分区键,特别是如果数据中有两个或更多重要的“维度”。换句话说,应用程序可能需要从不同角度高效、连贯地查看数据。这意味着你可能需要在系统内至少存储一些数据两次。
例如,你可能需要按照用户 ID 和帖子 ID 对博客应用程序的数据进行分片,因为这是应用程序查看数据的两种常见方式。想象一下:你经常想看到某个用户的所有帖子和某个帖子的所有评论。按用户分片无法帮助你找到帖子的评论,按帖子分片无法帮助你找到用户的帖子。如果你需要让这两种类型的查询仅涉及单个分片,那么你将需要双向分片。
仅仅因为你需要多个分区键,并不意味着你需要设计两个完全冗余的数据存储。让我们看另一个例子:一个社交网络读书俱乐部网站,用户可以在该网站上评论书籍。该网站可以显示一本书的所有评论,以及用户已阅读并评论的所有书籍。
你可以为用户数据构建一个分片数据存储,为书籍数据构建另一个。评论既有用户 ID 又有帖子 ID,因此它们跨越分片之间的边界。你可以将评论与用户数据一起存储,而只需将评论的标题和 ID 与书籍数据一起存储。这可能足以在不访问两个数据存储的情况下呈现大多数书籍评论的视图,如果需要显示完整的评论文本,可以从用户数据存储中检索。
跨片查询
大多数分片应用程序至少有一些需要从多个分片聚合或��接数据的查询。例如,如果读书俱乐部网站显示最受欢迎或活跃的用户,它必须根据定义访问每个分片。使这样的查询正常工作是实现数据分片最困难的部分,因为应用程序将一个查询视为单个查询,需要将其拆分并并行执行多个查询,每个查询对应一个分片。一个良好的数据库抽象层可以帮助减轻痛苦,但即使如此,这样的查询比分片内查询慢得多,成本更高,通常也需要积极的缓存。
如果你选择的分片方案使跨片查询成为异常而不是规范,那么你将知道你选择的分片方案是一个好的选择。你应该努力使你的查询尽可能简单,并且包含在一个分片中。对于那些需要一些跨片聚合的情况,我们建议将其作为应用程序逻辑的一部分。
跨片查询也可以从摘要表中受益。你可以通过遍历所有分片并在每个分片上存储结果的冗余数据来构建它们。如果在每个分片上复制数据太浪费,你可以将摘要表合并到另一个数据存储中,这样它们只存储一次。
非分片数据通常存储在全局节点中,并进行大量缓存以保护免受负载影响。
一些应用程序基本上使用随机分片,其中一致的数据分布很重要,或者当没有很好的分区键时。分布式搜索应用程序是一个很好的例子。在这种情况下,跨分片查询和聚合是常规操作,而不是例外。
在分片中,跨分片查询并不是唯一困难的事情。保持数据一致性也是困难的。跨分片的外键不起作用,因此正常解决方案是根据需要在应用程序中检查引用完整性或在分片内部使用外键,因为分片内部的一致性可能是最重要的事情。虽然可以使用XA 事务,但这在实践中并不常见,因为会增加开销。
您还可以设计定期运行的清理流程。例如,如果用户的读书俱乐部账户过期,您不必立即删除它。您可以编写一个定期作业,从每本书的分片中删除用户的评论,并构建一个定期运行的检查脚本,确保数据在分片之间保持一致。
现在我们已经解释了如何将数据分割到多个集群以及如何选择分区键的不同方式,让我们来介绍两种最受欢迎的开源工具,可以帮助促进分片和分区。
Vitess
Vitess 是用于 MySQL 的数据库集群系统。它起源于 YouTube,然后成为 PlanetScale,由 Jiten Vaidya 和 Sugu Sougoumarane 共同创立的一个独立产品和公司。
Vitess 提供了许多功能:
- 支持水平分片,包括对数据进行分片
- 拓扑管理
- 源节点故障转移管理
- 模式更改管理
- 连接池
- 查询重写
让我们探索 Vitess 的架构及其组件。
Vitess 架构概述
图 11-3 是来自 Vitess 网站的图表,展示了其架构的不同部分。
图 11-3. Vitess 架构图(改编自 vitess.io)
以下是一些你需要了解的术语:
Vitess pod
一组数据库的一般封装以及支持分片、拓扑管理、模式更改管理和应用程序访问这些数据库的 Vitess 相关部分。
VTGate
控制应用程序和运维人员访问数据库实例的服务,用于管理拓扑结构、添加节点或对部分数据进行分片。这类似于之前描述的架构中的负载均衡器。
VTTablet
在 Vitess 管理的每个数据库实例上运行的代理。它可以接收来自运维人员的数据库管理命令,并代表运维人员执行这些命令。
拓扑(元数据存储)
在给定 pod 中保存由 Vitess 管理的数据库实例的库存以及相关信息。
vtctl
用于对 Vitess pod 进行操作更改的命令行工具。
vtctld
用于相同管理操作的图形界面。
Vitess 的架构始于一个一致的拓扑存储,其中保存了所有集群、MySQL 实例和 vtgate 实例的定义。这个一致的元数据存储在管理拓扑变化中发挥着至关重要的作用。当运维人员想要对 Vitess 管理的集群的拓扑进行更改时,实际上是通过一个名为 vtctl 的服务向数据存储发送命令,然后将该命令的组件操作发送给 vtgate。
Vitess 提供了可以在 Kubernetes 中部署 vtgate 层和元数据存储的数据库运维人员。在像 Kubernetes 这样的平台中拥有其控制平面可以增加其对单点故障的弹性。
Vitess 最大的优势之一是其关于如何扩展 MySQL 的理念,其中包括以下内容:
偏好使用较小的实例
按功能、水平或两者分割您的数据。但是当发生故障时,较小的实例会导致较小的爆炸半径。
复制和自动写入故障转移以增加弹性
Vitess 不通过多写节点技巧承诺“100%在线写入”。相反,它自动化写入故障转移,并在故障转移期间管理拓扑变化和应用程序对数据库节点的访问,以使写入停机时间尽可能短。
使用半同步复制确保持久性
Vitess 强烈推荐使用半同步复制(与默认的异步相对)来确保在向应用程序确认写入之前,写入始终由数据库层中的多个节点持久化。这是一种以延迟为代价换取持久性保证的关键权衡,当 Vitess 需要在非计划方式下故障转移写入主机时,这种权衡会产生回报。
这些架构原则可以帮助您在业务流量呈指数增长时在基础设施的数据库层具有更多的弹性。无论您是否专门使用 Vitess 或其他解决方案作为架构的一部分,您都应该遵循这些最佳实践。
将您的堆栈迁移到 Vitess
Vitess 是一个用于运行数据库层的有主见的平台,而不是一个即插即用的解决方案。因此,在您将其作为数据库访问层之前,您需要深思熟虑地计划如何实施这样的过渡。
具体而言,在评估 Vitess 作为可能解决方案时,请务必考虑以下迁移步骤:
1. 测试并记录您为整个系统引入的延迟。
将像 Vitess 这样复杂的堆栈引入应用程序堆栈肯定会增加一定量的延迟,特别是考虑到半同步复制的执行。确保这种权衡得到充分记录和明确沟通,以便您的下游依赖在构建依赖于这种数据库架构的 SLO 时做出知情决策。
2. 使用金丝雀部署模型。
在生产中过渡期间,您可以将vttablet配置为“外部管理”。这允许vttablet和直接连接到数据库服务器,随着您逐渐通过应用程序节点群增加连接更改。
3. 开始分片。
一旦所有应用层访问都通过vtgate/vttablet而不是直接访问 MySQL,您就可以开始使用 Vitess 的完整功能集来将表拆分到新的集群中,将数据水平分片以获得更多的写入吞吐量,或者仅仅添加副本以获得更多的读取负载能力。⁶
Vitess 是一个强大的数据库访问和管理产品,它已经从早期在谷歌的日子里走过了很长的路。它已经证明了它能够实现戏剧性的增长和一个弹性的数据库基础设施。然而,这种强大和灵活性是以增加复杂性为代价的。Vitess 不像一个简单的负载均衡器通过流量,你应该权衡业务需求和引入和维护像 Vitess 这样复杂的数据库管理工具的成本。
ProxySQL
ProxySQL 专门为 MySQL 协议编写,并以 GPL 发布。René Cannaò,一个为许多公司提供咨询的 DBA 和长期的 MySQL 贡献者,是主要作者。现在它是一个提供 ProxySQL 产品付费支持和开发合同的全功能公司。
让我们深入了解一些关于其架构、配置模式、用例和功能的细节。
ProxySQL 架构概述
您可以将 ProxySQL 用作任何应用程序代码和 MySQL 实例之间的中间层。ProxySQL 为应用程序提供了一个基于会话的、基于 MySQL 协议的接口,用于与数据库交互。代替应用程序直接打开连接到数据库实例,ProxySQL 代表应用程序打开连接。
这种设计使代理对应用程序节点看起来是不可见的。其会话感知性允许在没有停机的情况下在 MySQL 实例之间移动这些连接。当您处理不再投资于的应用程序时,这尤其有用,因为您现在可以利用 ProxySQL 中的功能而无需对您可能不确定更改的代码进行任何更改。
ProxySQL 还提供强大的连接池。应用程序打开到 ProxySQL 的连接与 ProxySQL 打开到配置连接的数据库实例的连接是分开的。这种分离可以保护数据库实例免受应用层突发流量的影响。
当您有能力单独管理客户端连接与实际连接到数据库的连接数量时,您引入了以前没有的灵活性。现在您可以扩展应用程序节点池,而无需担心它会增加到数据库的连接负载超出您想要支持的范围。这允许在使用 ProxySQL 时解释常见模式时,应用程序和业务需求的多样化场景。
配置 ProxySQL
ProxySQL 在启动时使用配置文件,但在内存中和嵌入式 SQLite 文件中维护其运行时配置,您可以直接访问并使用管理界面查询。
ProxySQL 的管理界面允许您发出命令来更改运行配置,然后使用 MySQL 命令将新配置转储到磁盘以实现持久性。这使您可以对运行中的 ProxySQL 实例进行零停机更改。您还可以使用此管理界面来进行由配置管理或自动故障转移脚本发出的自动更改。您可以在图 11-4 中看到您的架构通常如何利用 ProxySQL 和服务发现来为服务提供强大的访问层。
警告
需要注意的是,虽然我们在此图中将 ProxySQL 显示为一个对象,但我们强烈建议在生产环境中利用其集群机制,并在给定堆栈中部署多个实例。永远不要运行单点故障(SPoF)。
图 11-4。应用程序节点、ProxySQL 和服务发现之间的交互(根据 Bill Sickles 的图表调整)
ProxySQL 对其连接的数据库进行独立和分层的健康检查。根据这些健康检查的结果,ProxySQL 添加或删除主机或调整流量权重。您可以指定复制延迟阈值、成功连接的时间以及连接失败时的重试次数等许多其他配置选项,以控制在服务和应用程序需求的背景下可接受的故障容忍度。这些配置选项允许 ProxySQL 对不响应的主机做出准确的反应,要么暂时删除后端数据库,然后稍后重复健康检查,要么完全删除挣扎的后端成员,直到操作员介入。
使用 ProxySQL 进行分片
ProxySQL 对于许多分片拓扑结构非常有用。虽然它不像 Vitess 那样自动分割数据,但它可以作为一个很好的轻量级中间层,具有分片感知能力,并可以相应地路由应用程序连接。让我们来看看你可以如何将其用作分片层的路由层。
按用户分片
如果你的数据按功能或业务功能在不同的数据库集群中分割,并且不同的应用程序群访问这些集群,你应该为每个应用程序使用完全不同的数据库凭据。ProxySQL 可以利用这个用户参数将流量路由到完全不同的后端数据库池,无论是写入还是读取。
您可以通过在其管理界面上运行这些命令,然后将更改保存到其磁盘配置文件中,来配置 ProxySQL 中的此类路由:
INSERT INTO mysql_users (username, password, active, default_hostgroup, comment) VALUES ('accounts', 'shard0_pass', 1, 0, 'Routed to the accounts shard'), ('transactions', 'shard1_pass', 1, 1, 'Routed to the transactions shard'), ('logging', 'shard2_pass', 1, 2, 'Routed to the logging shard'); LOAD MYSQL USERS RULES TO RUNTIME; SAVE MYSQL USERS RULES TO DISK;
提示
始终确保您保持 ProxySQL 的运��时配置和磁盘配置同步,以避免在 ProxySQL 进程重新启动时出现不愉快的惊喜。
这还可以方便地记录所有这些用户执行的操作以符合合规性,而不会对数据库造成任何负载。您将在第十三章中看到,我们还建议出于合规性原因为不同的数据库用户分别设置,因此这种设计也符合一些合规性目标。
按模式分片
您可以使用 ProxySQL 的模式名称作为管理流量路由规则来支持分片数据集的另一种方式。以下是您如何在 ProxySQL 的配置中定义的示例:
INSERT INTO mysql_query_rules (rule_id, active, schemaname, destination_hostgroup, apply) VALUES (1, 1, 'shard_0', 0, 1), (2, 1, 'shard_1', 1, 1), (3, 1, 'shard_2', 2, 1); LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;
请注意,只要正确命名模式,此配置可用于水平分片或功能分片。
在这种方式中使用 ProxySQL 时,我们最后一个重要建议是确保使用其原生的集群功能,这样可以确保像mysql_rules
这样的关键配置表在集群中的所有 ProxySQL 节点上同步,为中间件层提供冗余。
使用 ProxySQL 的其他好处
让我们讨论一些常见模式,在这些模式中使用 ProxySQL 可以帮助缓解快速增长环境中的常见问题。
在许多应用程序中,“向数据库打开更多连接”是我们在查询延迟开始上升时经常看到的模式。然而,在实践中,这可能导致停机⁷,并且倾向于使许多连接处于空闲状态,消耗资源但不执行任何工作。当应用程序层直接向数据库打开更多连接时,数据库服务器在连接管理上花费的资源量也会增加。这会导致数千个连接压倒已经超载的数据库实例。所有这些活动导致持续的停机时间,多个微服务中的级联故障以及延长的面向客户的影响。
ProxySQL 的连接管理架构通过仅向数据库打开可以工作的连接数量,有助于保护数据库层免受意外应用程序高峰的影响。ProxySQL 可以重用这些连接来处理不同的客户端请求。这种行为最大化了单个连接到数据库服务器可以完成的工作量,从而减少了管理连接的资源数量,并允许更有效地使用数据库服务器的内存资源。
ProxySQL 中的其他值得注意的功能
ProxySQL 在一般用途应用程序代理中具有一些突出的功能:
- 基于端口、用户或简单的正则表达式匹配的查询路由
- 前端应用程序连接和后端连接到数据库的 TLS 支持
- 支持各种 MySQL 版本,如 AWS Aurora、Galera Cluster 和 Clickhouse
- 连接镜像
- 结果集缓存
- 查询重写
- 审计日志
您可以通过访问其文档了解 ProxySQL 的广泛功能集(远远超出分片支持)。
ProxySQL 是一个强大的工具,您可以使用它来扩展应用程序,并为数据库层提供适当的性能保护,并具有支持各种业务需求的附加功能(如合规性、安全规则等)。如果您的公司发现自己处于高增长轨迹上,拥有一系列新的和不那么新的服务共享数据库资源,那么它可以是一个强大的工具,可以安全地继续这种增长。ProxySQL 提供了一个易于部署的抽象,可以比 HAProxy 更复杂,但在基础设施和复杂性方面的前期投资较少。然而,它也不提供 Vitess 中找到的一些更高级的功能,比如数据集的自动分片、模式更改的管理,以及VReplication,这是一个强大的工具,用于启用抽取、转换、加载(ETL)管道和更改数据流。
摘要
扩展 MySQL 是一场旅程。您应该在本章结束时更有准备地评估您的扩展需求,并了解如何扩展读取、如何扩展写入,以及通过向架构添加排队来使您的流量增长更可预测。您现在应该了解如何通过分片来扩展写入以及随之而来的所有复杂决策。
在深入研究可扩展性瓶颈之前,请确保您已经优化了您的查询,检查了您的索引,并为 MySQL 设置了稳固的配置。这可能为您提供计划更好的长期策略所需的时间。优化后,专注于确定您是读取密集型还是写入密集型,然后考虑哪些策略最适合解决任何即时问题。在规划解决方案时,请确保考虑如何为长期可扩展性做好准备。
对于读取密集型工作负载,我们建议转移到读取池,除非复制延迟是无法克服的问题。如果延迟是一个问题,或者如果您的问题是写入密集型的,您需要考虑分片作为下一步。
¹ 在物理科学中,单位时间的工作被称为功率,但在计算中,“功率”是一个如此多义的术语,以至于它是模棱两可的,我们要避免使用它。然而,容量的一个精确定义是系统的最大功率输出。
² 为了简化解释,我们选择忽略多个 CPU 和上下文切换的复杂性。
³ 这仍然不完全准确,因为当 CPU 接近 100%时,延迟会增加,您将无法再添加四千个查询。
⁴ 最常用的并且我们推荐的是Hashicorp 的 Consul。
⁵ 感谢 HiveDB 项目和布里特·克劳福德为提供这些优雅的图表。
⁶ 这种部署策略是由摩根·托克在 2019 年的Kubecon 演讲中详细解释的。
⁷ 欲了解更多信息,请参阅维基百科关于雷鸣群问题的条目。
第十二章:云中的 MySQL
很可能,你对于是否迁移到云服务提供商,甚至你的组织最终选择哪一个云服务提供商都没有太多控制权。你可以控制的是如何构建你的数据库环境。你可以选择两个方向:托管的 MySQL 或者在虚拟机上构建。托管的 MySQL 往往更加无需过多干预,但通常更昂贵且控制权更少。在虚拟机上构建意味着你可以更加灵活地构建和观察你的平台,但需要更多的时间和运营开销。
在本章中,我们将概述托管 MySQL 的主要选项以及它们对你有何用处。我们还将解释如何开始构建一个虚拟机选项,包括选择正确的规格和磁盘类型,以及在云中运行 MySQL 时需要准备的操作复杂性(如主机重启)。
警告
我们不会涵盖云服务提供商提供的 bug。这些提供的产品在不断发展,因此我们建议你保持与动态来源(如新闻通讯或 bug 论坛)的同步,而不是依赖像本书这样的静态参考。
托管的 MySQL
云服务提供商中的托管 MySQL 提供了许多便利,帮助团队在产品增长和功能集扩展时减少操作 MySQL 的认知负担。每个公共云都有自己对于托管 SQL 数据库应该是什么样子以及如何工作的理解。亚马逊网络服务(AWS)提供了几种 Aurora MySQL 的版本(我们很快会详细讨论这些),谷歌云平台(GCP)有 Cloud SQL,几乎所有公共云提供商都提供类似的服务。
托管解决方案的主要吸引力在于它们提供了一个可访问的数据库设置,无需深入了解 MySQL 的具体细节。通过几次点击或terraform apply
,你就可以在线创建一个带有副本和定期备份的数据库,然后就可以开始了。对于想要快速入门的公司或团队来说,这可能是一个非常吸引人的选择。
另一方面,使用托管的 MySQL 会缺乏很多可见性和控制权。你无法访问操作系统或文件系统,并且在进程本身内部的操作受到限制。除了云服务提供商提供的内容,你无法检查系统的其他任何内容。在大多数情况下,如果遇到问题,你只能提交支持工单并等待回复。你无法设置任何高级拓扑结构,备份和恢复方法也受限于云服务提供商提供的内容。
值得注意的是,许多云服务提供商提供的是与 MySQL 兼容的数据存储。这是一个具有 SQL 接口但内部工作方式可能与本书关注的 Oracle MySQL 完全不同的数据存储。我们将介绍一般的权衡和每个托管解决方案的不同之处,以帮助你选择最适合你的团队和业务需求的选项。
亚马逊 Aurora for MySQL
Aurora MySQL 是一个与 MySQL 兼容的托管数据库。Aurora 最吸引人的卖点是它将计算与存储分离,这使得它们可以分别和更灵活地扩展。Aurora 管理了许多你通常需要处理的运营任务,比如执行快照备份、管理快速模式更改、审计日志和管理单一区域内的复制。
Aurora MySQL 还有不同的提供方式。我们将简要介绍这些提供方式之间的区别。
标准的 Aurora 提供的是长时间运行的计算实例,你可以选择一个实例类别(就像在运行自己的 MySQL 时一样),并且你会获得内部复制到六个副本的附加存储。
警告
在撰写本文时,AWS 认为 Aurora 快速 DDL 是一个“实验模式”功能。如果您正在阅读本文,情况仍然如此,我们建议您参考第六章以了解有关使用数据库外部工具进行在线模式更改的更多信息。
需要注意的是,在 Aurora 集群内部的复制完全是亚马逊专有的,不是我们在 Oracle MySQL 中所知道和使用的复制。由于集群中所有的 Aurora 实例共享相同的存储层来访问数据,集群内部的复制是使用块存储来完成的。¹ 然而,Aurora 确实支持以我们在社区服务器中熟悉的格式写入二进制日志,以便团队希望将数据从一个 Aurora 集群复制到另一个集群或者出于其他二进制日志的目的,比如变更数据捕获。²
提示
如果您打算将任何关键数据库放在 Aurora 上,我们强烈建议您考虑使用亚马逊的 RDS 代理来管理应用程序与 Aurora 的通信方式。在您知道应用程序端可能会出现新连接风暴的情况下,RDS 代理可以非常方便地防止新连接量影响数据库。
自 2015 年 Aurora MySQL 出现以来,AWS 已经扩展了 Aurora MySQL 的选项,以满足更广泛的用例和业务需求:
Aurora 无服务器
Aurora MySQL 的无服务器产品消除了长时间运行的计算,并利用亚马逊的无服务器平台来提供数据库的计算层。如果您的工作负载不需要持续运行,这将为您提供很大的成本灵活性。
Aurora 全局数据库
这是 Aurora 为那些需要在多个地理区域中可用数据但不想使用二进制日志复制手动管理从主要集群到其他区域集群获取数据更改的解决方案。请注意,这会带来一些权衡,您应始终参考亚马逊的文档,以确保您接受正确的权衡。
Aurora 多主
多主是 Aurora 集群的一种特殊类型,可以同时在多个计算节点上接受写入。它旨在作为一个高可用解决方案,其中单个区域中的写入可用性是最高优先级。请注意,Aurora 多主带有自己的一套限制。首先,在撰写本文时,它运行的是 MySQL 5.6 服务器核心,这将阻止您使用许多功能。集群中允许的节点数量有一个最大限制,并且您不能在同一部署中混合多主和全局数据库。我们认为 Aurora 多主是一个非常主观的解决方案,适用于您在每个数据存储和应用程序交互中拥有的可用性和一致性选择,并建议您在选择之前仔细考虑您所陈述的约束和权衡。
AWS 继续对其托管的关系型数据库产品进行更新和改进,因此我们将避免深入讨论 Aurora 各种版本之间的功能差异。然而,图 12-1 提供了一个流程图,帮助您了解哪种类型的 Aurora 可能最适合您的需求以及在什么权衡下。
图 12-1 为您提供了一个基本的决策树,帮助您在 Aurora 选项之间做出选择。重要的是,尽管 Aurora 有许多选项,但总会有权衡。例如,您无法同时实现多写高可用性和跨区域亚秒级复制。但您可以利用这些产品来展示这些权衡,并推动关于哪个更重要:写入可用性还是区域复制的困难产品讨论。
Aurora 并不是唯一由云提供商提供的托管 MySQL 产品。GCP 有自己的产品。
图 12-1. 一个流程图,帮助您选择适合您需求的 Aurora 版本
高性能 MySQL 第四版(GPT 重译)(四)(3)https://developer.aliyun.com/article/1484357