概要
应用程序正在变得越来越好,它拥有更多的功能、更多的活跃用户,并且每天都会收集更多的数据。但数据库现在导致应用程序的其余部分变慢。数据库分片可能是问题的答案,但许多人不知道它是什么,最重要的是何时使用它。在本文中我们将讨论什么是数据库分片、它的工作原理以及使用它的最佳方法。
在我们讨论这个问题之前,有必要了解为什么我们对数据存储进行分片,以及在开始分片之前可以选择的各种选项。
当表达到特定大小时,人们通常会觉得分片是解决所有扩展问题的神奇解决方案。然而拥有包含数十亿行的表,并且没有看到令人信服的理由进行分片,因为使用模式非常适合单个表,并且没有看到任何强有力的理由(除了管理如此大的表之外)对表进行分片。
什么是数据库分片?
简单来说分片是一种跨多台机器分布数据的方法。当没有一台机器可以处理预期的工作负载时,分片变得特别方便。
分片是水平扩展的一个例子,而垂直扩展是一个越来越大的机器来支持新工作负载的例子。
工程师经常会陷入以最复杂的方式做事的过程中,但是随着应用程序的发展变得更加容易,早期保持事情简单会使以后的事情变得具有挑战性。因此,如果问题通过获得具有更多资源的计算机而消失,这就是正确的答案。
现在我们已经讨论了潜在的服务器架构,让我们谈谈数据布局。
还可以通过多种方式对数据进行分区,并将特定表移动到其数据库,这与在微服务架构中看到的非常相似,其中应用程序的特定方面拥有其数据库服务器。应用程序知道在哪里寻找每个。或者可以跨多个数据库节点存储同一个表的行,这带来了诸如分片键之类的想法;稍后会详细介绍。
Cassandra 等更现代的数据库将其从应用程序逻辑中抽象出来,并在数据库级别进行维护。
分片之前有哪些选择?
与任何分布式架构一样,数据库分片也需要花钱。设置分片、保持每个分片上的数据最新以及确保请求发送到正确的分片既耗时又复杂。在开始分片之前可能想看看这些其他选项之一是否适合。
选项 1:什么也不做。
曾多次被问到在没有任何明显的瓶颈或限制因素(例如耗尽可以支持工作负载的硬件)的情况下,分片是否是一个好主意。如果没有损坏,就不要修理它。
选项 2:垂直缩放
之前我们已经回避了这一点,只是让机器拥有更多资源,添加额外的 RAM,为计算繁重的工作负载添加更多的 CPU 核心,并添加额外的存储。这些都是不需要重新设计应用程序和数据库架构的选项。其他最终限制,例如带宽(网络或系统内部),也可能迫使进行分片。
WAL WAL(预写日志)是磁盘上只能添加的额外结构。在将更改写入数据库之前,首先将它们写入日志,该日志必须位于持久存储上。它用于从崩溃和丢失的事务中恢复。此日志还用于支持某些数据库(例如 PostgreSQL 和 MySQL)中的复制。
选项 3:复制
如果对数据所做的大部分操作都是读取数据,那么复制可以提高数据的可用性并加快读取数据的速度。这可以避免数据库分片的一些复杂性。通过制作更多数据库副本可以提高读取性能。当然假设已经补充了缓存,这可以通过负载平衡或根据查询所在的位置路由查询来完成。但复制使得写入密集型工作负载更难处理,因为每次写入都必须复制到每个节点。这可能会根据数据存储而有所不同,其中一些数据存储是异步执行的,而其他数据存储可能会延迟初始写入以确保其被复制。
选项 4:专业数据库
性能不佳是由于数据库需要针对其所服务的工作负载进行更好的设计而导致的。例如将搜索数据存储在关系数据存储中可能没有什么意义。将类似的东西转移到 Elasticsearch 会更有效。将 blob 移动到像 S3 这样的对象存储比将它们存储在关系存储中可能是一个巨大的胜利。外包此功能可能比尝试对整个数据库进行分片更有意义。
如果应用程序数据库管理大量数据、需要大量读取和写入和/或需要始终可用,则分片数据库可能是最佳选择。让我们看一下分片的优点和缺点。
如果必须的话进行分片
分片可以在提高系统吞吐量、存储容量和可用性方面提供几乎无限的可扩展性。有许多更小、更容易控制的系统,每个系统都可以通过各自的副本自行扩展和缩小。
所有这些优点都是以操作复杂性、应用程序开销和支持这种新设计的基础设施成本为代价的。
它是如何工作的?
在对数据库进行分片之前,我们需要回答一些重要的问题。计划将取决于如何回答这些问题。
-
• 我们如何跨分片分布数据?如果数据分布不均匀,是否存在潜在的热点?
-
• 我们运行哪些查询以及表如何交互?
-
• 数据将如何增长?以后需要如何重新分配?
在我们进入下一步之前,了解以下术语很重要。
分片键是主键的一部分,它告诉数据应该如何分布。使用分片键可以通过将操作路由到正确的数据库来快速查找和更改数据。
同一节点包含具有相同分片键的条目。共享相同分片键的一组数据称为逻辑分片。一个数据库节点中包含多个逻辑分片,也称为物理分片。
最关键的假设,也是未来最难改变的假设。逻辑分片只能跨越一个节点,因为它是存储的原子单元。在分片对于单个节点来说太大的情况下,数据库集群实际上会出现空间不足的情况。
基于键的分片
算法分片数据库使用哈希函数来定位数据。这允许我们给定一个特定的分片键来找到正确的物理分片来请求数据。
数据仅通过哈希函数进行分发。它不考虑有效负载的大小或空间使用情况。散列的好处是当没有合适的分区键可用时,可以实现更均匀的分布,并且如果有正确的分区键,则可以动态计算位置。
这种分片策略的缺点是,重新分片数据可能很困难,并且在可用的情况下保持一致性更加困难。
基于范围的分片
基于范围的分片,根据某个值的范围将数据分为块。
这需要一个查找表来查看数据应存储在哪里。保持这样一个表格的一致性并显然在这里选择范围是至关重要的。
为这种分片类型选择分片键时,必须选择基数高的分片键,因此该键的可能值有很多。例如,可能值为 North、South、East 和 West 的键的基数较低,因为只有 4 个。
如果一切都落在可能值的 50% 内,某些分片将开始出现热点。使用准确的数据运行此实验很容易,只需几行代码即可完成。首先选择一个键和范围并检查潜在的分布。
基于关系的分片
这种共享机制将相关数据保存在单个物理分片上。例如,相关数据通常分布在关系数据库中的多个表中。
例如对于像 Instagram 这样的应用程序,用户和所有相关数据将被分片到同一个物理节点,其中分别包含帖子和评论。通过将相关实体放在同一分区中,可以从单个分区中获得更多信息。因此在整个物理分片中保持了更强的一致性,并减少了跨物理分片的查询。
跨分片事务
最后介绍一些有关复杂性的细节,当需要执行跨多个分片的事务时,可能会引入这些复杂性。无论计划多少,寿命足够长的服务或应用程序最终都会遇到一些跨分片事务。
这本质上意味着需要由符合 ACID 的数据库提供的事务保证,但在数据库无法确保这种合规性的分片中,因为操作的数据超出了启动它的事务的范围。
这通常称为全局事务,其中多个子事务需要协调并成功。一般来说,事务打开的时间越长,可能发生的争用和潜在的失败就越多。
两阶段提交
两阶段提交理论上很简单,但在实践中很难执行。
-
• Leader 写入一条持久的交易记录,指示跨分片事务。
-
• 参与者将他们的承诺意愿永久记录下来并通知领导者。
-
• 领导者在收到所有响应后通过更新持久事务记录来提交事务。(如果没有人响应,它可以中止事务。)
-
• 在领导者宣布提交决定后,参与者可以显示新的状态。(如果领导者中止事务,他们会删除暂存状态。)
协议路径中的读写放大是一个主要问题。发生写入放大是因为您必须写入事务记录并持久地暂存提交,这需要每个参与者至少进行一次写入。过多的写入可能会导致锁争用和应用程序不稳定。数据库必须另外过滤每个读取,以确保它不会看到任何依赖于待处理的跨分片事务的状态,这会影响系统中的所有读取,甚至是非事务性读取。
结论
我们讨论了分片、何时使用它以及如何设置它。对于需要管理大量数据并使其易于进行大量读写的应用程序来说,分片是一个出色的解决方案。尽管如此这仍然使操作变得更加复杂。在开始实施之前应该考虑收益是否值得付出的成本,或者是否有更直接的解决方案。
欢迎点赞收藏转发,感谢🙏