当前位置: 首页 > news >正文

第11章1 扩展 MySQL

        上一篇:《第10章-3 Percona XtraBackjiezh》,接着了解如何进行扩展MySql

        在个人项目或者说在一家初创公司中运行MySQL,与在一个拥有成熟市场和呈“曲棍球棒式增长”(形容高速增长)的企业中运行它非常不同。在高速的业务环境中,流量可能逐年增长几个数量级,环境会变得更加复杂,随之而来的数据需求也会快速增加。扩展MySQL与其他类型的服务器非常不同,这在很大程度上是因为数据是有状态的。如果要扩展We b服务器,常见的方式就是在负载均衡的后端添加更多的服务器节点,而这通常就是扩展We b服务器的全部工作。

        在本章中,我们将解释可扩展性意味着什么,并带你了解可能需要扩展的不同维度。我们将探讨为什么读可扩展是必不可少的,并展示如何安全地完成扩展,以及通过排队等策略使扩展写入更可预测。最后,我们介绍通过使用像ProxySQL和Vitess这样的工具将数据集进行分片以达到扩展写入的目的。在阅读完本章时,你应该能够确定系统有哪些周期性模型,以及如何去扩展读和写。

什么是扩展?

        可扩展性是系统支撑不断增长的流量的能力。一个系统扩展能力的好坏可以用成本和简单性来衡量。如果增加系统的扩展能力十分昂贵或复杂,那么在达到天花板时可能需要花费更多的努力来解决相关问题。

        容量是一个和可扩展性相关的概念。系统容量表示在一定时间内能够完成的工作量 (从物理学来看,单位时间内做的功称为功率(power),而在计算机领域,“power”是一个被用在多处的术语,有多种含义,因此应避免使用它。但是关于容量的精确定义是系统的最大输出功率) ,但容量必须是可以被有效利用的。系统的最大吞吐量并不等同于容量。大多数基准测试能够衡量一个系统的最大吞吐量,但真实的系统一般不会被使用到极限。如果达到最大吞吐量,则性能会下降,响应时间会变得不可接受且非常不稳定。我们将系统的真实容量定义为在保证可接受性能的情况下能够达到的吞吐量。

容量和可扩展性并不依赖于性能。以高速公路上的汽车来类比的话:

        ● 系统是高速公路,包含许多车道和汽车。

        ● 性能是汽车的速度。

        ● 容量是车道数乘以最大安全速度。

        ● 可扩展性是在不减慢速度的前提下,能够增加更多车辆和车道的程度。

        在这个类比中,可扩展性依赖于多个条件,如换道设计是否合理、路上有多少车辆发生事故或抛锚,以及汽车行驶速度是否不同或是否频繁变道——但一般来说,和汽车的引擎是否强大无关。这并不是说性能不重要,性能确实重要,只是需要指出,即使系统性能不是很高也可以具备可扩展性。

        从更高的视角来看,可扩展性就是能够通过增加资源来提升容量的能力。即使MySQL架构是可扩展的,但应用本身也可能无法扩展,如果很难增加容量,不管原因是什么,应用都是不可扩展的。之前我们以吞吐量来定义容量,但同样也需要从更高的层面来看待容量问题。从这个角度来看,容量可以简单地被认为是处理负载的能力,从几个不同的角度来考虑负载很有帮助。

数据量

        应用所能累积的大量数据是可扩展性最普遍的一个挑战,特别是对于现在的很多Web应用而言,因为这些应用从不删除任何数据。例如社交网站通常不会删除以前的消息或评论。

用户数

        即使每个用户只有少量数据,如果用户很多,数据量也会累积起来,而且数据量的增长速度会超过用户数量的增长速度。更多的用户意味着要处理更多的事务,并且事务数可能和用户数不成比例。最后,大量用户(以及更多的数据)也意味着更多复杂的查询,特别是当查询依赖于用户间的关系时(关系的数量可以用N*(N-1)/2来计算,这里N表示用户数)。

用户活跃度

        不是所有的用户的活跃度都相同,并且用户活跃度也不总是不变的。如果用户突然变得活跃,例如,由于增加了一个吸引人的新特性,那么负载可能会明显提升。用户活跃度不仅仅指页面浏览量,而且即使页面浏览量相同,如果网站中需要大量工作才能生成的部分变得更受欢迎,那么也可能导致更多的工作。另外,某些用户会比其他用户更活跃:他们可能比一般人有更多的朋友、消息和照片。

相关数据集的大小

        如果用户间存在关系,应用可能需要在整个相关用户群体间执行查询和计算,这比处理单个用户及其数据要复杂得多。社交网站经常会面临由那些人气很旺的用户组或朋友很多的用户所带来的挑战。

        扩展性的挑战可能来自多个方面。在下一节,我们将讨论如何确定瓶颈在哪里,以及如何解决瓶颈。

读取受限与写入受限的工作负载

        在考虑扩展数据库基础架构时,首先要审视的问题是,需要扩展读限制工作负载还是写限制工作负载。读限制工作负载是指读取(SELECT)总流量超过服务器容量的工作负载,写限制工作负载则超过了服务器提供DML(INSERT、UPDATE、DELETE)操作的容量。理解你正在对抗的目标包括理解系统的工作负载。

理解你的工作负载

        数据库工作负载包含很多内容。首先是容量,如我们在前面提到的,它是对一段时间内工作的衡量。对于数据库来说,这通常可以归结为每秒的请求量,工作负载的定义之一是系统能达到的QPS数。然而,不要对此感到失望。在20%使用率的CPU下,1000 QPS并不意味着还可以增加4000 QPS,因为并非每个查询都是相等的。

        查询有各种形式:读取、写入、主键查找、子查询、联接、批量插入等。每一种都有相应的成本,成本以CPU时间或响应延迟来衡量。当查询等待磁盘返回信息的时间较长时,该时间被计入成本(简单起见,为解释这个问题,我们选择忽略多个CPU核数和上下文切换的复杂性)。

        了解资源的容量很重要,比如有多少个CPU,磁盘读写IOPS、吞吐量的限制是多少,以及网络吞吐量是多少。这其中每一个都会对延迟产生影响,而延迟将直接关系到工作负载。

        工作负载是所有类型的查询及其延迟的混合。如果我们用20%的CPU处理1000 QPS,只要它们的延迟相同,我们就可以再增加4000 QPS(这仍然不是完全准确的,因为随着CPU的使用率接近100%,延迟就会增加,并将无法再添加4000个查询),这听起来很合理。如果我们真的引入4000个查询,并触及磁盘IOPS瓶颈,那么所有读操作的延迟都会增加。

        如果仅仅可以访问基本的系统指标,如CPU、内存和磁盘相关指标,那么你几乎无法理解正在影响哪个指标,你需要确定读和写的性能如何。我们在第3章的“检查读写性能”一节中提供了一个例子,通过那个示例,可以确定读取和写入的延迟。如果结合时间来对这些数字进行趋势分析,可以看到读或写的延迟是否正在增加,以及哪里可能因此受到限制。

读取限制工作负载

        假设,在设计产品时,您采用了一个源主机用于所有数据库流量的捷径。增加更多应用节点可能会扩展为客户端提供请求,但最终将受到您的单一源数据库主机响应这些读取请求的能力的限制。这的主要指标是 CPU 利用率。高 CPU 意味着服务器花费所有时间处理查询。CPU 利用率越高,您在查询中看到的延迟就越多。然而,这并不是唯一的指标。您还可以看到大量的磁盘读取 IOPS 或吞吐量,表明您经常访问磁盘或从磁盘读取大量行。

        通过添加索引、优化查询和缓存可缓存的数据,您可以最初改善这一点。一旦您没有更多的改进空间,您将面临一个读取限制的工作负载,这就是使用副本扩展读取流量的时候。我们将在本章后面讨论如何使用读取副本池扩展您的读取量,如何为这些池运行健康检查,以及在开始使用该架构时要避免的陷阱。

写入限制工作负载

你也可能会遇到写限制负载,下面是一些关于写限制数据库负载的示例:

        ● 注册人数或许正以指数级增长。

        ● 现在是电子商务旺季,销售额和订单数量都在增长。

        ● 现在是选举季,存在大量的竞选宣传。

        这都是业务用例,它们都会导致数据库的写入量呈指数级增加,必须对数据库进行扩展。同样,一个单源数据库,即使可以垂直扩展一段时间,也只能到此为止。当写入量成为瓶颈时,必须开始考虑使用拆分数据的方法,以便在单独的子数据集上接受并行的写入。我们也将在本章后面讨论如何进行分片来达到写扩展的目的。

        这是一个合乎逻辑的设问,“如果我同时看到这两种类型的增长呢?”此时需要仔细检查schema,确定是否存在读需求增长比其他写需求增长更快的表数据子集。试图同时为这两者扩展数据库集群会带来很多痛苦和事故。我们建议将表分离到不同的功能集群中,以独立地扩展读取和写入,这是更有效地使用读池来扩展读取流量的先决条件。

        既然已经确定了是读限制负载还是写限制负载,接下来我们将讨论如何以有效的方式帮助指导数据的功能拆分。

功能分片

        基于业务中的“功能”来拆分数据是一项和业务背景强相关的任务,需要深入了解数据的用途。这需要与流行的软件架构模式相结合,如面向服务架构(SOA)和微服务。并非所有按功能拆分的方式都相同,在极端情况下,如果要把每张表都放在独立的“功能”数据库中,可能会由于过多的碎片而令事情变得更糟。

        如何恰当地将大型的整体/混合数据库拆分为一组合理的较小集群,以帮助业务扩展?以下是一些需要记住的指导原则:

        ● 不要根据工程团队的组织架构进行拆分,它会经常变动。

        ● 根据业务功能来拆分表。支撑账户注册的表可以与负责现有客户设置的表分开,而支持新功能的表应该在单独的数据库中开发。

        ● 不要回避处理数据中混杂了不同业务关系的问题,你不仅需要倡导数据分离,还需要倡导应用程序重构,并需要引入API来实现相互跨界的访问。我们常见的一个例子是将客户身份与其账单混合在一起。

        通常一开始会有一些明确业务功能及访问模式的表,因此分离为单独的集群是一个容易实现的目标,但随着业务发展,这种分离会变得越来越不一样。

        现在我们已经基于业务功能以一种深思熟虑的方式分割数据,接下来让我们讨论如何使用副本读池来进行扩展以应对读限制负载的问题。

使用只读池扩展读取

        集群中的副本可用于多个目的。首先,在当前源节点出于任何原因需要停止服务时,副本是故障切换的候选对象,无论切换的方式是有计划的还是无计划的。由于这些副本也在不断地运行更新以匹配源节点中的数据,因此也可以使用它们来处理读取请求。

                在图11-1中,可以看到这个具有读副本池的新型架构的全貌。

        为了简单起见,我们假设应用程序节点仍然通过直连源数据库的方式来完成写入请求。稍后我们将讨论为何连接到源节点能更好地进行扩展。但是,请注意,相同的应用程序节点连接到一个虚拟I P,该虚拟I P充当节点和读副本之间的中间层。这就是副本读池,用来将不断增长的读负载扩展到多台主机。你可能还会注意到,并非所有的复制副本都在池中,这是一种防止不同的读取工作负载相互影响的常见方法。如果你有报告进程,或你的备份进程倾向于消耗所有磁盘I/O资源并导致复制延迟,则可以预留出一个或多个副本节点来完成这些任务,并将其排除在服务于面向客户流量的读池之外。或者,也可以通过复制检查来增强负载均衡器的健康检查机制,自动从读池中移除落后的节点,并在其赶上复制进度时重新引入。当应用程序需要从单点读取时,将读副本转换为可替换资源的灵活性会显著提升,并且可以在不影响到客户的前提下无缝地管理这些资源。

使用读池时会有不止一台服务读请求的数据库主机,为使生产环境顺利运行,有几点需要考虑:

        ● 如何将流量路由到读副本?

        ● 如何均匀地分配负载?

        ● 如何进行健康检查并移除不健康或延迟的副本,以避免提供陈旧的数据?

        ● 如何避免因意外地删除所有节点而对应用程序流量造成更多损害?

        ● 如何手动移除服务器以进行维护?

        ● 如何将新配置的服务器添加到负载均衡器中?

        ● 有哪些自动检查可避免在负载均衡器准备就绪之前添加新配置的节点?

        ● 对“准备好迎接新节点”的定义是否足够明确?

        管理这些读池的一种非常常见的方法是使用负载均衡器来提供虚拟I P,该I P充当所有要访问读副本的流量的中介。实现这一目的的技术包括HAProxy、自用主机时的硬件负载均衡器,或在公共云环境中运行时的网络负载均衡器。在使用HAProxy的情况下,所有应用程序主机将连接到一个“前端”,然后HAProxy负责将这些请求引导到后端定义的读副本。

        下面是一个HAProxy的配置文件示例,它定义了一个虚拟的IP前端,并将其映射到作为后端池的多个读副本:

globallog 127.0.0.1 local0 noticeuser haproxygroup haproxydefaultslog globalretries 2timeout connect 3000timeout server 5000timeout client 5000listen mysql-readpoolbind 127.0.0.1:3306mode tcpoption mysql-check user haproxy_checkbalance leastconnserver mysql-1 10.0.0.1:3306 checkserver mysql-2 10.0.0.2:3306 check

        通常,你可以使用配置管理来自动填充这样的配置文件。在进行配置时需要注意几点:在MySQL中,建议使用leastconn实现池节点之间的平衡;在负载升高时,像“轮询”这样的随机平衡策略将无助于使用未过载的主机;确保在MySQL实例上创建了正确的数据库用户以运行健康检查,否则所有节点都将被标记为不正常。

        一些用于数据分片的工具,如Vitess和ProxySQL,也可以充当负载均衡器的角色。我们将在本章的最后介绍这些工具。

管理读池的配置

        现在,在应用程序节点和副本之间有一扇“门”,你需要一种方法来使用选择的负载均衡器,以便轻松管理读池中包含或未包含的节点。你不会希望这是一个手动管理的配置。你已经走上扩展到大量数据库实例的道路,手动管理配置文件可能会导致错误、响应时间变长和主机故障,而且根本无法扩展。

        服务发现是一个很好的选择,它可以自动发现新的主机并将其加入池列表。这可能意味着,如果服务发现可用的话,可将服务发现解决方案部署为技术堆栈的一部分,或者依赖于云服务商的托管服务发现选项。这里需要注意的重点是,应确保这些情况读副本符合对应读池这个前提条件成立。理想情况下,可以将源节点和一到多个专门用于报表的副本排除在外。但也许还需要有更复杂的考虑,比如是否进一步分割副本以服务于不同应用程序的读取负载。我们建议除了备份/报表服务器或源节点,每个副本池至少还要有三个节点服务于特定应用。

        无论是运行自己的服务发现 (最常用的也是我们推荐的方案是来自Hashicorp 的 Consul(参见链接39 Consul by HashiCorp)),还是使用云服务商提供的方案,都应该意识到该服务的保障。不管是运行服务发现还是与一个团队协同工作,都需要考虑以下几点:

        ● 需要多久能检测到主机的故障?

        ● 数据传输速率如何?

        ● 当有数据库实例宕机时,如何刷新负载均衡器上的配置?

        ● 数据库的成员变更是作为后台进程处理的,还是需要切断现有的连接?

        ● 如果服务发现自身宕机,会发生什么呢?这是否会损害任何新建的数据库连接,或仅会导致负载均衡器成员身份的错误更改?在这种情况下,可以手动更改吗?

        灵活性带来了复杂性,因此必须在两者之间进行权衡,以便在生产中出现故障时能有最好的结果。要做的是始终将决定与所追求的SLI和SLO联系起来,而不是实现一个不切实际的100%正常运行时间的目标。

        现在,当需要增加或者减少读节点的时候,就知道该如何配置和更新实例了。

读取池的健康检查

        此时,对于判断读副本是否健康并已准备好接受来自应用程序的流量读取请求,需要考虑一个合理标准。这些标准可以是简单的“数据库进程启动并运行,端口正常响应”,但也可以更加复杂,如“数据库启动,复制延迟不能超过30秒,并且读请求的执行延时不能超过100毫秒”。

        提示:检查变量read_only和super_read_only的状态,以确保负载均衡器配置的读池成员都是真正的副本。

        决定这些健康检查能做到什么程度时,需要与应用程序开发团队进行讨论,以便每个人对从数据库中读取时所期望的行为都能有相同的理解和认识。以下是一些需要与团队交流的问题,它们可以用来帮助指导这个决策过程:

        ● 可以接受数据“过期”多长时间?如果返回几分钟前的数据,会有什么影响?

        ● 应用程序可接受的最大查询延迟是多久?

        ● 是否有读请求的重试逻辑,如果有,它是怎样的逻辑,以及它是幂等补偿的吗?

        ● 是否已为应用程序定义SLO?SLO倾向于查询延迟,还是仅关心正常运行时间?

        ●  当出现数据缺失时,系统的行为是怎样的?这种退化是可以接受的吗?如果可接受,能接受其持续多久?

        在很多情况下,只需通过端口检查来确认,MySQL进程是活动的且可以接受连接,就可以了。这意味着只要数据库在运行,就会成为池的一部分并为请求提供服务。

        但是,有时也需要更复杂的检测,因为所涉及的数据集非常重要,当复制延迟超过几秒或复制根本没有运行时,便不希望让其提供服务。对于这些场景,仍然可以使用读池,但需要使用HTTP检查来增强健康检查。此工作方式是,让选择的负载均衡器运行一个命令(通常是一个脚本),并根据响应代码确定节点是否正常。例如,在HAProxy中,后端将有这样的代码行:

option httpchk GET /check-lag

        这行代码表示,对于读池中的每台主机,负载均衡器将使用GET调用来调用路径/check-lag,并检查响应代码。该路径运行的脚本包含关于多久延迟可接受的逻辑。脚本将现有滞后状态与阈值进行比较,负载均衡器根据比较结果判断副本是否健康。

        尽管健康检查是一个强大的工具,但要小心使用那些具有复杂逻辑的工具(如之前描述的滞后检查),并确保有一个计划,可以确定在池中所有副本都没有通过健康检查时执行什么操作。可以有一个静态的“回退(fallback)”池,在出现某些全局故障(例如,整个集群滞后)时将所有节点重新引入,以避免意外地中断所有读取请求。有关一家公司如何加强这一点的更多细节,请参阅GitHub 博客上的这篇文章。

选择负载均衡算法

        有许多不同的算法可以确定哪台服务器应该接收下一个连接。每个供应商使用不同的术语,下面提供了一些可用的概念。

随机

        负载均衡器将每个请求定向到从可用服务器池中随机选择的服务器。

轮询

        负载均衡器以重复的顺序向服务器发送请求,如,A、B、C、A、B、C,等等。

最少连接

        下一个连接指向拥有最少活跃连接的服务器。

最快响应

        处理请求最快的服务器接收下一个连接。当池中包含快慢不一的机器时,这能很好地工作。然而,当查询复杂度变化很大时,处理SQL语句会很棘手。即使是相同的查询,在不同情况下的执行表现也会大不相同,比如当查询缓存为其提供服务时,或者当服务器的缓存已经包含了所需的数据时。

哈希

        负载均衡器对连接的源IP地址进行哈希处理,这会将地址映射到池中的一台服务器。当每次连接请求来自同一IP地址时,负载均衡器都会将其发送到同一台服务器。只有当池中的机器数量发生变化时,绑定才会更改。

权重

        负载均衡器可以组合几种算法并添加权重。例如,你可能有单CPU和双CPU机器,双CPU机器的能力大约是单CPU机器的两倍,因此可以通知负载均衡器向双CPU机器发送平均请求数两倍的请求。

        MySQL的最佳算法取决于具体工作负载。例如,当将新服务器添加到可用服务器池中时,使用最少连接算法可能会导致连接在新服务器缓存未预热之前大量涌入。

        你需要进行实验以找到适合工作负载的最佳性能。一定要考虑在特殊情况下和日常情况下会发生什么,以便在某些特殊的情况下——例如,在高查询负载期间,当进行schema更改时,或者当异常数量的服务器脱机时——你至少能承担一些糟糕的后果。

        我们在这里只描述了即时资源调度算法,它们不对连接请求进行排队处理。有时,使用排队算法可能更有效。例如,一个算法可能会在数据库服务器上维护一个给定的并发度,比如不允许同时超过N个活动事务。如果有太多的活动事务,该算法可以将新的请求放入队列,并根据条件由第一个变为“可用”的服务器为其提供服务。有一些连接池支持排队算法。

        到此,我们已经讨论了如何扩展读负载以及如何进行健康检查,现在是时候讨论如何扩展写入了。在了解如何直接扩展写入之前,可以先看看排队机制能使哪些地方的写流量增长更易管理。接下来我们讨论排队是如何帮助扩展写性能的。

        上一篇:《第10章-3 Percona XtraBackup》

        下一篇:《第11章-2 使用分片扩展写入》

相关文章:

  • Linux连接服务器全攻略:从基础到进阶
  • Hadoop架构与核心模块解析
  • hadoop纠删码基本原理
  • Vue3监听对象数组属性变化方法
  • Qwen-Agent的使用示例-天气查询
  • 记录 | Android TextView 中的滚动方向
  • 常见小问题(Open Folder as PyCharm Project)
  • 重构开发范式!飞算JavaAI革新Spring Cloud分布式系统开发
  • OpenGL Chan视频学习-7 How I Deal with Shaders in OpenGL
  • 打造现代 Web 服务的终极选择:轻量级 Rust HTTP 框架
  • MyBatis 核心组件剖析:架构、协作与源码解读
  • NLP学习路线图(八):常见算法-线性回归、逻辑回归、决策树
  • AI时代新词-Transformer架构:开启AI新时代的关键技术
  • rpm安装jenkins-2.452
  • 深度解析 vm.max_map_count:用途、原理与调优建议
  • 如何用ChatGPT提升学术长文质量
  • VR 技术在农业领域或许是一抹新曙光​
  • VR 展厅开启一场穿越时空的邂逅​
  • Java 访问者模式深度重构:从静态类型到动态行为的响应式设计实践
  • 力扣HOT100之回溯:22. 括号生成
  • 济南哪家公司做网站好/企业邮箱入口
  • 辽宁省网站建设/seo网络推广是干嘛的
  • 天津做网站印标/杭州推广系统
  • 网站建设 发票/广州seo技术优化网站seo
  • 福州网站制作有限公司/360优化大师旧版本
  • 织梦cms瀑布流极品美女图片网站源码/搜索大全