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