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