MongoDB分片是什么以及最佳实践?

如何扩展MongoDB?最佳分片实践是什么?

尽管灵活的模式是大多数人熟悉的MongoDB 的方式之一,但在处理非常庞大的数据集时,它也是最好的数据库之一(也许甚至是最好的数据库)。尽管这个论点的证明需要一篇完整的文章(希望有一天我能找到时间写),但总的想法是基于SQL的解决方案不支持分片,并且在你自己构建它时非常糟糕。

你能希望的最好的情况是创建一个集群(顺便说一下,这与分片基本上没有关系),或者选择像亚马逊的RDS或谷歌的Cloud SQL这样的托管解决方案,但随着数据的增长,费用也会变得非常昂贵。

在本文中,我们将介绍一种用于MongoDB的水平数据库扩展的基本技术:分片,并为此提供一些建议的最佳实践。然而,我认为最好从分片的基础知识开始,因为许多寻求扩展MongoDB的人可能对此并不非常熟悉。

然而,如果您了解分片,可以随意略过下一节。

分片的基础知识

你可能已经注意到上一节中的最后一段中使用了“水平”一词。在不再进入另一个大的弯路之前,我想快速提出这一点。扩展被认为有两种类型:要么使用更强大的具有更高存储容量的机器(垂直),要么连接多台较小的计算机并形成一个集合(水平)。

现在,鉴于目前最好的服务器RAM不超过256 GB或硬盘不超过16 TB,当试图进行垂直扩展(或者术语所说的“扩大规模”)时,很快就会遇到瓶颈。然而,您可以连接许多单台机器在一起(至少在理论上如此)并轻松绕过此限制。

当然,现在的挑战是在所有这些机器之间进行协调。

数据库分片

术语“sharding” generally applies to databases的含义是单台机器永远不足以保存所有数据。在分片时,数据库被“分割”成存储在不同机器上的不同块。一个简单的例子可能是:假设一个企业的机器可以存储多达200万个客户数据项。现在,该企业正在接近这个临界点,并很可能很快就会超过250万个用户。因此,他们决定将他们的数据库分成两个:

神奇的是,系统容量现在增加了一倍!

嗯,如果生活只是那么简单! :)

数据库分片中的挑战

当你深入考虑分片时,一些邪恶的挑战会浮现出来。

没有主键

一旦您退出单个数据库,主键就失去了意义。例如,如果您的主键设置为自增,然后将一半的数据移动到另一个数据库,您现在将为每个主键拥有两个不同的数据项。

没有外键

由于数据库没有支持指向当前数据库之外实体的功能(好吧,即使是同一台机器上的不同数据库也不受支持,更别说在不同的机器上的数据库了),外键的概念也被扔掉了。突然间,数据库变得“愚蠢”,数据完整性成了你的问题。

奇怪的数据错误

如果一台机器出现故障,最终用户可以看到一个“糟糕,出了点问题!”的页面,这无疑会让人恼火,但是过一段时间后一切又会恢复正常。

现在考虑一下在分片数据库中会发生什么。假设我们先前示例中的分片数据库是一个银行数据库,一个客户正在向另一个客户汇款。让我们还假设第一个客户的数据存储在第一个分片中,而第二个客户的数据存储在第二个分片中(你看出我想说什么了吗?!)。如果包含第二个分片的机器出现故障,你能想象系统将处于什么状态吗?交易的钱将去哪里?第一个用户会看到什么?第二个用户会看到什么?当分片重新联机时,他们都会看到什么?

事务管理

我们还要考虑事务管理这个至关重要的情况。这次,假设系统工作得非常好。现在,两个人(A和B)向第三个人(C)付款。很可能两个交易都会同时读取C的账户余额,导致发生混乱:

  • C的账户余额为100美元。
  • A的交易读取C的余额:100美元。
  • B的交易读取C的余额:100美元。
  • A的交易增加50美元并更新余额:100 + 50 = 150美元。
  • B的交易增加50美元并更新余额:100 + 50 = 150美元。

该死!50美元就这样消失了!

传统的SQL系统通过提供内置的事务管理可以解决这个问题,但是一旦你走出单个机器的范围,你就会遇到麻烦。

问题是,使用这样的系统很容易遇到无法恢复的数据损坏问题。拔掉你的头发也于事无补! 🙂

MongoDB分片

对于软件架构师来说,对MongoDB的兴奋点并不在于其灵活的模式,而在于其内置的分片支持。只需几个简单的规则和连接的机器,你就可以很快地运行一个MongoDB分片集群。

下面的图片展示了在典型的Web应用部署中的情况。

图片来源:mongodb.com

MongoDB分片的最大优点是即使分片的平衡也是自动完成的。也就是说,如果你有五个分片,其中两个接近空,你可以告诉MongoDB重新平衡,使得所有分片都是均匀填满的。

作为开发人员或管理员,你不需要太担心,因为MongoDB在幕后会完成大部分繁重的工作。同样,部分节点的故障也适用;如果你在集群上正确配置并运行了复制集,部分宕机不会影响系统的正常运行时间。

整个解释会变得相当简短,所以我会在本节结束时说一句,MongoDB具有几个内置工具用于分片、复制和恢复,使开发人员能够轻松构建大规模应用程序。如果你想了解更全面的MongoDB分片功能指南,可以查看 official docs

查看这个实用的 implement Sharding 指南。

你可能还对这个 complete developer’s guide 感兴趣。

MongoDB分片最佳实践

虽然MongoDB在分片方面“开箱即用”,但这并不意味着我们可以得过且过。分片可以使你的项目成败得失,这取决于它是否做得好或不好。

此外,还有许多小细节需要考虑,如果处理不当,项目很容易崩溃。我的目的不是吓唬你,而是强调需要进行计划,并在做出小决定时要非常小心。

分片键不可避免地控制着MongoDB中的分片,所以最好从这个方面开始我们的调查。

高基数

基数意味着变化的数量。例如,一个拥有100万人口的喜爱国家的集合将具有较低的变化(世界上只有那么多国家!),而一个包含他们电子邮件地址的集合将具有(完全)高基数。为什么这很重要?假设您选择了一个天真的方案,根据用户的名字分片数据。

这里有一个相当简单的安排;传入的文档会被扫描以查找用户名,并根据英文字母表中的第一个字母所在位置,落入三个分片中的一个。类似地,搜索文档也很容易:例如,“Peter”的详细信息肯定在第二个分片中。

这听起来都很好,但关键是,我们无法控制传入文档用户的姓名。如果我们大部分时间只得到B到F范围的名字怎么办?如果是这样,我们将在shard1中有一个所谓的“巨大”块:系统数据的大部分将会挤在那里,有效地将设置变成一个单一的数据库系统。

解决方法?

选择具有高基数的键——例如用户的电子邮件地址,或者您甚至可以选择复合分片键,即多个字段的组合。

单调变化

MongoDB分片中的一个常见错误是使用单调递增(或自增)键作为分片键。

通常使用文档的主键。这里的想法是善意的,即随着不断地创建新文档,它们将平均地落入可用的一个分片中。不幸的是,这种配置是一个经典的错误。原因是如果分片键始终在增加,那么在某个点之后,数据将开始积累在分片的高值一侧,导致系统不平衡。

图片来源:mongodb.com

正如您在图像中所看到的,一旦我们超过20范围,所有文档都开始在Chunk C中积累,导致那里形成一个整体。解决办法是选择使用散列分片键方案,该方案通过对提供的字段之一进行散列来创建分片键,并使用该键来确定块。

图片来源:Mongodb.com

散列分片键看起来像这样:

{
    "_id" :"6b85117af532da651cc912cd"
}

……可以通过在Mongo客户端shell中使用以下命令来创建:

db.collection.createIndex( { _id: hashedValue } )

早期分片

从战壕中直接获得的最有用的建议之一是尽早进行分片,即使最终只得到一个小型的两块集群。一旦数据超过500 GB或其他某个临界值,MongoDB中的分片过程将变得混乱,您应该准备好遭遇令人讨厌的意外。此外,重新平衡过程会消耗非常高的网络带宽,如果不小心,可能会使系统瘫痪。

然而,并不是每个人都支持分片。作为一个有趣的例子(真正的学习在评论中),请参阅这个不错的Percona链接。

运行平衡器

另一个好主意是监控您的流量模式,并仅在低流量时运行分片平衡器。正如我之前提到的那样,重新平衡本身需要相当大的带宽,这可能会迅速将整个系统变得非常缓慢。请记住,不平衡的分片不是立即引起恐慌的原因。只需让正常使用继续,等待低流量机会,然后让平衡器完成其余的工作!

下面是您可能可以完成的操作(假设您在凌晨3点到5点之间有低流量):

use config 
db.settings.update( 
   { _id: "balancer" }, 
   { $set: { activeWindow : { start : "03:00", stop : "05:00" } } }, 
   { upsert: true } 
)

结论

分片和扩展任何数据库都是一项棘手的任务,但幸运的是,与其他流行数据库相比,MongoDB使其更易管理。

确实有一段时间,由于其几个关键问题和默认行为,MongoDB对于任何项目都不是正确的选择,但那些问题早已过去。除了分片、重新平衡、自动压缩、聚合级分布式锁等众多功能外,MongoDB已经走在了前面,成为软件架构师的首选。

我希望本文能够对MongoDB中的分片是什么以及开发人员在扩展时需要注意什么有所启发。接下来,熟悉流行的MongoDB commands

类似文章