如果您的应用程序遇到负载问题,请抽出香槟! 您的网络应用必须相当成功才能进入这一阶段。 您已经达到了应用程序可以处理的用户数量,事情开始变慢并且出错。 网络请求开始超时,数据库查询执行需要一段时间,页面加载缓慢。 恭喜!您的应用已准备好进行扩展! 但是,该放下香槟了……您需要处理这些不断增长的痛苦,直到用户离开您的应用程序为止,否则竞争对手就会复制您的想法。
扩展成本
在垂直,水平和由内而外*分割数据库之前,应牢记一个重要原则。 您不应该实施过早的优化或尝试在实际需要时扩展应用程序。 实施伸缩解决方案带来以下复杂性:
- 添加新功能需要更长的时间
- 系统变得越来越复杂,涉及更多的零件和变量
- 代码可能更难测试
- 查找和解决错误变得更加困难
仅当您的应用程序处于满负荷状态时,才应接受这些折衷。 保持系统简单,除非有保证,否则不要引入扩展复杂性。
由内而外的数据库分片不是真正的解决方案。 关键是有各种各样的扩展解决方案,除非需要,否则不要实施它们!
使用指标查找瓶颈
每个应用程序/系统都不同,要确定实施哪种扩展解决方案,您必须首先确定瓶颈在哪里。 是时候检查您的资源监视系统,或者如果尚未创建一个。 无论您使用的堆栈如何,都有可用的工具来监视您的资源。 如果您在任何领先的IaaS(基础设施即服务)提供商(例如AWS,Microsoft Azure和GCP)上运行,都有出色的应用程序性能管理工具可供选择。
这些工具通过图形和其他数据可视化方法说明了资源的性能。 使用这些图形来查找峰值或平坦的顶部。 这些通常意味着资源不堪重负或已满负荷,并且无法处理新的操作。 如果显然没有任何容量,但是您的应用程序运行缓慢,请尝试在频繁使用的操作中分散日志。 检查日志中是否需要花费很长时间才能通过网络加载资源,可能是其他服务器(例如第三方API)或您的数据库服务器引入了延迟。 您应该将数据库托管在另一台服务器上,如果是这种情况,那么还应该检查该计算机的资源监控。
通过考虑用户如何使用您的应用程序,以及从逻辑上考虑开始显示的错误或裂缝,确定瓶颈在哪里很简单。 以Twitter为例,这个特定的平台主要用于读取和编写推文。 如果Twitter的监视服务表明与这些操作有关的数据库负担沉重,则对他们的团队来说,开始优化平台的这一领域是有意义的。 在本文中,我们将深入研究数据库扩展解决方案,这通常是失败的第一点。 如果您还不熟悉系统设计,请阅读简短的文章,向您介绍该主题。 我建议在实施扩展解决方案之前对系统设计有所了解。
从鸟瞰视角扩展应用程序
现在我们对问题/瓶颈所在/位置有一个很好的认识,我们可以开始实施解决方案以解决这些问题。 记住,简单是关键,我们要始终避免引入不必要的复杂性。
扩展解决方案的高层目标是使堆栈减少应用程序最常见请求的工作,或有效地将无法消除的工作负载分配到多个资源中。 缩放技术执行此操作的方式通常会转换为以下一项或多项:
- 重用应用程序已查找的数据
- 消除了客户端对应用程序已经拥有的数据的请求
- 存储常见操作的结果,以减少重复计算
- 在请求-响应周期中避免复杂的操作
许多扩展技术可以归结为某种形式的缓存。 过去,记忆是昂贵而稀缺的。 如今,将其添加到服务器已经很便宜。 与磁盘或网络相比,内存访问数据的速度要快许多个数量级。 在这个用户有太多选择的时代,再加上我们的关注度极低,速度和性能对于您的应用程序的生存至关重要。
数据库扩展解决方案
1. 缓存数据库查询
缓存数据库查询是可以处理数据库负载的最简单的改进之一。通常,应用程序将包含少数查询,这些查询构成了大多数请求。不必每次都在网络上对该数据进行往返,而是可以将其简单地缓存在Web服务器的内存中。第一个请求将从数据库中获取数据,并将结果缓存在服务器上,以后的请求将从缓存中读取。由于数据花费在网络上的时间更少,并且距离客户端更近,因此可以提高性能。
由于大量的工作负载分配给了缓存系统,因此还导致更多的数据库服务器资源可用。除了提高可用性之外,如果数据库不可用,则高速缓存仍可以为应用程序提供连续服务,从而使系统对故障的恢复能力更强。您可以使用许多工具对数据库查询日志进行分析,因此您可以查看哪些查询花费的时间最长,哪些查询运行的频率最高。
显然,缓存的数据会很快变得"陈旧"或过时。 您将必须选择要缓存的数据以及要保留多长时间。 例如,在线报纸每24小时就会有一份新的日报,而不是每次用户访问该网站时都从数据库中请求该数据,而是可以将这些数据在Web服务器上缓存24小时并直接从服务器提供该数据。 。 产品或业务要求将决定哪些内容可以缓存,哪些内容不能缓存。
2. 数据库索引
数据库索引是一种提高数据库表上数据检索操作速度的技术。 索引用于快速定位数据,而不必每次访问表时都在表中搜索每一行。 通常,数据库索引的数据结构将是二进制搜索树。 这允许将访问数据的时间复杂度从线性时间O(n)降低到对数时间Olog(n)。
根据表中的行数,这可以节省大量使用索引列的查询的时间。 例如,如果您有10,000个用户,并且您的应用程序的配置文件页面按用户名查找用户,则未编制索引的查询将检查users表中的每一行,直到找到与传递给查询的用户名匹配的配置文件 。 这可能需要多达10,000个行检查O(n)。 通过为"用户名"列创建索引,数据库可以在对数时间复杂度(Olog(n))下提取该行。 在这种情况下,行检查的最大数量将是14,而不是10,000!
有效的索引编制通过提高效率来减轻数据库的负载,这还可以显着提高性能,从而带来更好的用户体验。 创建索引确实会添加另一组要存储在数据库中的数据,因此在确定要索引的字段时必须谨慎判断。 即使使用了现有的存储空间,索引也还是很值得的,尤其是在现代开发中,内存便宜且性能是生存不可或缺的一部分。
在本节中略微提到了时间复杂度和数据结构,但没有进行详尽的解释。 如果您有兴趣学习或希望对时间复杂性和数据结构有所了解,那么上面链接的文章非常有用!
3. 会话存储
许多应用程序通过将会话ID存储在cookie中,然后将每个会话的键/值对的实际数据存储在数据库表中来处理会话。 这可能会成为对数据库的大量读取和写入。 如果会话数据使数据库不堪重负,那么最好重新考虑如何以及在何处存储该数据。
将会话数据移动到内存缓存工具(例如redis或memcached)可能是一个不错的选择。 由于内存中的内存比大多数数据库使用的持久性磁盘存储要快,因此这将减轻数据库中会话数据的负担,并提高访问速度。 但是,由于内存是易失性内存,因此如果缓存系统脱机,则存在丢失所有会话数据的风险。
您也可以考虑将会话实现更改为将会话信息存储在cookie本身中,这将使您保持会话状态的方法从服务器移到客户端。 JWT是这种模式最流行的实现。 这将减轻数据库中所有会话数据的负担,并消除服务器端会话的依赖性,尽管这会带来一系列挑战。
4. 从站主复制
如果即使在缓存通用查询,创建有效索引以及处理会话存储之后,数据库仍然承受着来自读取的过多负载,那么复制可能是下一个最佳选择。
使用从属主复制,您只有一个数据库可以写入。 它被克隆到您读取的几个(根据需要)从数据库中,每个从数据库都位于另一台计算机上(请参见下图)。 这样可以减轻主数据库的读取负担,并将其分配到多个服务器上。 该模型还提高了写操作的性能,因为主设备专门用于写操作,同时由于从设备分布在不同区域,因此可以显着提高读取速度并减少延迟。
由于每个从数据库都在另一台计算机上,因此对主数据库的写入需要传播到从数据库,这可能导致数据不一致。 如果您需要立即读取写入数据库的数据,例如您正在更新配置文件并希望立即呈现它,则可以选择从master数据库读取。 从属主复制是一个功能非常强大的扩展解决方案,但是它具有相当多的复杂性。 在用尽了更简单的解决方案并确保在应用程序内进行有效优化之后,实施此解决方案是明智的。
5. 数据库分片
到目前为止,这些扩展解决方案中的大多数都专注于通过管理对数据库的读取来减少负载。 数据库分片是一种水平扩展解决方案,可通过管理对数据库的读写来管理负载。 这是一种架构模式,涉及将主数据库拆分(分区)为多个数据库(分片)的过程,这些数据库可以更快,更易于管理。
数据库分片技术有两种类型:垂直分片和水平分片(请参见下图)。 使用水平分区时,将表取出并放在不同的机器上,每个表具有相同的列,但行不同。 垂直分区更为复杂,其中涉及在多台计算机之间拆分一个表。 一个表被分离出来并放入新的不同表中。 一个垂直分区中保存的数据独立于所有其他分区中的数据,每个表都包含不同的行和列。
两种分片技术都有助于水平扩展,也称为"向外扩展",这使您可以在系统中添加更多机器以分配/分散负载。 水平扩展通常与垂直扩展(也称为"向上扩展")形成对比,后者涉及升级现有服务器的硬件。 扩展数据库相对简单,尽管任何非分布式数据库在计算能力和存储方面都有其局限性,因此拥有自由扩展的自由度可使您的系统更加灵活。
分片的数据库体系结构还可以显着提高应用程序查询的速度,并提供增强的故障恢复能力。 在未分片的数据库上提交查询时,它可能不得不搜索表中的每一行,这可能会非常慢。 或者,通过将一个表拆分为多个表,查询必须遍历更少的记录才能返回结果。 由于每个表都在单独的服务器上,因此减轻了服务器不可用带来的影响。 对于分片的数据库,与未分片的数据库相比,中断的影响可能仅影响单个分片,在未分片的数据库中,中断可能使整个应用程序不可用。
具有分片的数据库体系结构可带来一些巨大的好处,但是,它很复杂且实现和维护成本很高。 在用完其他扩展解决方案之后,绝对可以考虑使用此选项,因为无效实施的后果可能非常严重。
结论
恭喜,您的应用程序现在已经有了适当的解决方案,可以成功处理数据库负载并随着应用程序的成功进行扩展! 尽管还没有足够的时间来庆祝……有效扩展的服务器是高性能和可靠应用程序不可或缺的一部分。