介绍
任何看到显着增长的应用程序或网站最终都需要扩展以适应流量的增加。 对于数据驱动的应用程序和网站,以确保数据安全性和完整性的方式进行扩展至关重要。 很难预测一个网站或应用程序将变得多么流行,或者它将保持这种流行度多久,这就是为什么一些组织选择允许他们动态扩展数据库的数据库架构的原因。
在这篇概念性文章中,我们将讨论一种这样的数据库架构:分片数据库。 近年来,分片受到了很多关注,但许多人并没有清楚地了解它是什么,或者对数据库进行分片可能有意义的场景。 我们将讨论什么是分片,它的一些主要优点和缺点,以及一些常见的分片方法。
什么是分片?
分片是一种与 水平分区 相关的数据库架构模式——将一个表的行分成多个不同的表(称为分区)的做法。 每个分区都有相同的模式和列,但也有完全不同的行。 同样,每个分区中保存的数据都是唯一的,并且独立于其他分区中保存的数据。
考虑水平分区与 垂直分区 的关系可能会有所帮助。 在垂直分区的表中,整个列被分离出来并放入新的、不同的表中。 一个垂直分区中保存的数据独立于所有其他分区中的数据,并且每个分区都包含不同的行和列。 下图说明了如何对表进行水平和垂直分区:
分片涉及将一个数据分成两个或更多个更小的块,称为 逻辑分片 。 然后逻辑分片分布在不同的数据库节点上,称为物理分片,可以容纳多个逻辑分片。 尽管如此,所有分片中保存的数据共同代表了整个逻辑数据集。
数据库分片体现了 无共享架构 。 这意味着分片是自治的; 它们不共享任何相同的数据或计算资源。 但是,在某些情况下,将某些表复制到每个分片中以用作参考表可能是有意义的。 例如,假设有一个应用程序的数据库,该数据库依赖于重量测量的固定转换率。 通过将包含必要转换率数据的表复制到每个分片中,这将有助于确保查询所需的所有数据都保存在每个分片中。
通常,分片是在应用程序级别实现的,这意味着应用程序包含定义将读取和写入传输到哪个分片的代码。 但是,一些数据库管理系统内置了分片功能,允许您直接在数据库级别实现分片。
鉴于分片的一般概述,让我们回顾一下与此数据库架构相关的一些正面和负面影响。
分片的好处
分片数据库的主要吸引力在于它可以帮助促进水平扩展,也称为横向扩展。 水平扩展是向现有堆栈添加更多机器以分散负载并允许更多流量和更快处理的做法。 这通常与 垂直扩展 形成对比,也称为 扩展 ,它涉及升级现有服务器的硬件,通常通过添加更多 RAM 或 CPU。
在单台机器上运行关系数据库并通过升级其计算资源根据需要对其进行扩展是相对简单的。 但最终,任何非分布式数据库在存储和计算能力方面都将受到限制,因此具有水平扩展的自由度会使您的设置更加灵活。
有些人可能选择分片数据库架构的另一个原因是加快查询响应时间。 当您在尚未分片的数据库上提交查询时,它可能必须搜索您正在查询的表中的每一行,然后才能找到您要查找的结果集。 对于具有大型单体数据库的应用程序,查询可能会变得异常缓慢。 但是,通过将一个表分片为多个表,查询必须经过更少的行,并且它们的结果集会更快地返回。
分片还可以通过减轻中断的影响来帮助使应用程序更加可靠。 如果您的应用程序或网站依赖于非分片数据库,则中断有可能使整个应用程序不可用。 但是,对于分片数据库,中断可能只会影响单个分片。 尽管这可能会使应用程序或网站的某些部分对某些用户不可用,但总体影响仍然小于整个数据库崩溃的情况。
分片的缺点
虽然对数据库进行分片可以使扩展更容易并提高性能,但它也会施加某些限制。 在这里,我们将讨论其中的一些,以及为什么它们可能是完全避免分片的原因。
人们遇到分片的第一个困难是正确实现分片数据库架构的复杂性。 如果操作不当,分片过程可能会导致数据丢失或表损坏的风险很大。 但是,即使做得正确,分片也可能会对团队的工作流程产生重大影响。 用户必须跨多个分片位置管理数据,而不是从单个入口点访问和管理数据,这可能会对某些团队造成破坏。
用户在对数据库进行分片后有时会遇到的一个问题是分片最终会变得不平衡。 例如,假设您有一个包含两个独立分片的数据库,一个用于姓氏以字母 A 到 M 开头的客户,另一个用于姓氏以字母 N 到 Z 开头的客户。 但是,您的应用程序为大量姓氏以字母 G 开头的人提供服务。 因此,AM 分片逐渐累积比 NZ 分片更多的数据,导致应用程序变慢并为很大一部分用户停止。 AM 分片已成为所谓的 数据库热点 。 在这种情况下,分片数据库的任何好处都被减速和崩溃所抵消。 数据库可能需要修复和重新分片以实现更均匀的数据分布。
另一个主要缺点是,一旦数据库被分片,就很难将其恢复到未分片的架构。 在分片之前对数据库进行的任何备份都不会包含自分区以来写入的数据。 因此,重建原始的非分片架构需要将新的分区数据与旧备份合并,或者将分区的数据库转换回单个数据库,这两种方式都将是昂贵且耗时的工作。
最后一个需要考虑的缺点是,并非每个数据库引擎都原生支持分片。 例如,尽管可以手动对 PostgreSQL 数据库进行分片,但 PostgreSQL 不包含自动分片功能。 有许多 Postgres 分支确实包含自动分片,但这些分支通常落后于最新的 PostgreSQL 版本并且缺乏某些其他特性。 一些专门的数据库技术——如 MySQL Cluster 或某些数据库即服务产品,如 MongoDB Atlas——确实包含自动分片作为一项功能,但这些数据库管理系统的普通版本没有。 因此,分片通常需要“自己动手”的方法。 这意味着通常很难找到分片文档或故障排除提示。
当然,这些只是分片前需要考虑的一些一般性问题。 根据其用例,对数据库进行分片可能还有更多潜在的缺点。
既然我们已经介绍了分片的一些缺点和好处,我们将讨论分片数据库的几种不同架构。
分片架构
一旦你决定对数据库进行分片,接下来你需要弄清楚你将如何去做。 在运行查询或将传入数据分发到分片表或数据库时,将数据发送到正确的分片至关重要。 否则,可能会导致数据丢失或查询速度非常慢。 在本节中,我们将介绍一些常见的分片架构,每种架构都使用稍微不同的过程来跨分片分发数据。
基于密钥的分片
基于密钥的分片,也称为基于哈希的分片,涉及使用从新写入的数据中获取的值——例如客户的ID号、客户端应用程序的IP地址、邮政编码、等等 — 并将其插入 哈希函数 以确定数据应该转到哪个分片。 哈希函数是将一段数据(例如,客户电子邮件)作为输入并输出离散值的函数,称为 哈希值 。 在分片的情况下,哈希值是一个分片 ID,用于确定传入数据将存储在哪个分片上。 总而言之,该过程如下所示:
为了确保条目以一致的方式放置在正确的分片中,输入到哈希函数中的值都应该来自同一列。 此列称为 分片键 。 简而言之,分片键类似于 主键 ,它们都是用于为各个行建立唯一标识符的列。 从广义上讲,分片键应该是静态的,这意味着它不应该包含可能随时间变化的值。 否则,它将增加进入更新操作的工作量,并可能降低性能。
虽然基于键的分片是一种相当常见的分片架构,但在尝试向数据库动态添加或删除其他服务器时,它可能会使事情变得棘手。 当您添加服务器时,每个服务器都需要一个相应的哈希值,并且您现有的许多条目(如果不是全部的话)都需要重新映射到它们新的、正确的哈希值,然后迁移到适当的服务器。 当您开始重新平衡数据时,新的和旧的散列函数都将无效。 因此,您的服务器将无法在迁移期间写入任何新数据,并且您的应用程序可能会停机。
该策略的主要吸引力在于它可以用于均匀分布数据以防止热点。 此外,由于它通过算法分配数据,因此无需维护所有数据所在位置的地图,这对于其他策略(如基于范围或基于目录的分片)是必需的。
基于范围的分片
基于范围的分片涉及基于给定值的范围对数据进行分片。 举例来说,假设您有一个数据库,该数据库存储有关零售商目录中所有产品的信息。 您可以创建几个不同的分片,并根据它们所处的价格范围划分每个产品的信息,如下所示:
基于范围的分片的主要好处是实现起来相对简单。 每个分片都保存一组不同的数据,但它们都具有相同的架构,以及原始数据库。 应用程序代码读取数据属于哪个范围并将其写入相应的分片。
另一方面,基于范围的分片并不能防止数据分布不均,从而导致上述数据库热点。 查看示例图,即使每个分片拥有相同数量的数据,特定产品也有可能比其他产品受到更多关注。 反过来,它们各自的分片将收到不成比例的读取次数。
基于目录的分片
要实现基于 目录的分片 ,必须创建并维护一个 查找表 ,该表使用分片键来跟踪哪个分片保存哪些数据。 查找表是一个包含一组静态信息的表,其中包含有关在何处可以找到特定数据的信息。 下图显示了基于目录的分片的简单示例:
在这里,Delivery Zone 列被定义为一个分片键。 来自分片键的数据连同每一行应写入的任何分片一起写入查找表。 这类似于基于范围的分片,但不是确定分片键的数据属于哪个范围,而是将每个键绑定到自己的特定分片。 在分片键具有低基数的情况下,基于目录的分片是基于范围的分片的一个不错的选择——这意味着它具有少量可能的值——并且分片存储一系列键没有意义。 请注意,它也不同于基于键的分片,因为它不通过散列函数处理分片键; 它只是根据查找表检查密钥以查看需要写入数据的位置。
基于目录的分片的主要吸引力在于它的灵活性。 基于范围的分片架构限制您指定值的范围,而基于键的分片架构限制您使用固定的哈希函数,如前所述,以后可能很难更改。 另一方面,基于目录的分片允许您使用任何您想要将数据条目分配给分片的系统或算法,并且使用这种方法动态添加分片相对容易。
虽然基于目录的分片是此处讨论的最灵活的分片方法,但在每次查询或写入之前需要连接到查找表可能会对应用程序的性能产生不利影响。 此外,查找表可能成为单点故障:如果它被损坏或以其他方式失败,它可能会影响一个人写入新数据或访问其现有数据的能力。
我应该分片吗?
是否应该实现分片数据库架构几乎总是一个争论的问题。 一些人认为分片是达到一定规模的数据库的必然结果,而另一些人则认为这是一个令人头疼的问题,除非绝对必要,否则由于分片增加了操作复杂性,因此应该避免。
由于这种增加的复杂性,通常仅在处理大量数据时才执行分片。 以下是对数据库进行分片可能有益的一些常见场景:
- 应用程序数据量增长到超过单个数据库节点的存储容量。
- 对数据库的写入或读取量超过了单个节点或其只读副本可以处理的量,从而导致响应时间变慢或超时。
- 应用程序所需的网络带宽超过了单个数据库节点和任何只读副本可用的带宽,从而导致响应时间变慢或超时。
在分片之前,您应该用尽所有其他优化数据库的选项。 您可能需要考虑的一些优化包括:
- 设置远程数据库。 如果您正在使用所有组件都驻留在同一台服务器上的单体应用程序,您可以通过将数据库移到自己的机器上来提高数据库的性能。 这不会像分片那样增加复杂性,因为数据库的表保持不变。 但是,它仍然允许您将数据库与基础设施的其余部分分开垂直扩展。
- 实现缓存。 如果您的应用程序的读取性能是给您带来麻烦的原因,那么缓存是一种有助于改进它的策略。 缓存涉及将已请求的数据临时存储在内存中,以便您以后更快地访问它。
- 创建一个或多个只读副本。 另一种有助于提高读取性能的策略,这涉及将数据从一个数据库服务器(主服务器)复制到一个或多个辅助服务器。 在此之后,每次新的写入都会先发送到主服务器,然后再复制到辅助服务器,而读取则专门针对辅助服务器。 像这样分布读写可以防止任何一台机器承担过多的负载,有助于防止减速和崩溃。 请注意,创建只读副本涉及更多的计算资源,因此需要花费更多的钱,这对某些人来说可能是一个重大限制。
- 升级到更大的服务器。 在大多数情况下,将一个数据库服务器扩展到具有更多资源的机器比分片需要更少的工作量。 与创建只读副本一样,具有更多资源的升级服务器可能会花费更多资金。 因此,只有当它真的最终成为您的最佳选择时,您才应该进行调整大小。
请记住,如果您的应用程序或网站增长超过某个点,这些策略都不足以单独提高性能。 在这种情况下,分片可能确实是您的最佳选择。
结论
对于那些希望水平扩展数据库的人来说,分片可能是一个很好的解决方案。 但是,它也增加了很多复杂性,并为您的应用程序创建了更多潜在的故障点。 对某些人来说,分片可能是必要的,但创建和维护分片架构所需的时间和资源可能会超过对其他人的好处。
通过阅读这篇概念性文章,您应该对分片的优缺点有了更清晰的了解。 展望未来,您可以利用这种洞察力就分片数据库架构是否适合您的应用程序做出更明智的决定。