在 MongoDB 中快速搜索数十亿个小文档的策略

Posted

技术标签:

【中文标题】在 MongoDB 中快速搜索数十亿个小文档的策略【英文标题】:Strategies for fast searches of billions of small documents in MongoDB 【发布时间】:2013-07-19 02:57:33 【问题描述】:

我需要存储数十亿个小型数据结构(每个大约 200 字节)。到目前为止,将每个元素存储为单独的文档效果很好,Mongo 每秒可提供大约 10,000 个结果。我使用 20 字节散列作为每个文档的 _id,并在 _id 字段上使用单个索引。在测试中,这适用于包含 5,000,000 个文档的数据集。

在运行中,我们将每秒发出大约 10,000 个请求,每秒更新大约 1,000 次现有文档,每秒插入新文档 100 次或更少。

当我们无法在 RAM 中存储整个索引时,我们如何管理更大的数据集?如果我们将多个元素组合到每个文档中,MongoDB 的性能是否会更好——通过索引更快地搜索,但在每个查询中返回更多数据?

与其他关于 SO 的问题不同,我不仅对我们可以向 Mongo 中填充多少数据感兴趣。它可以清楚地管理我们正在查看的数据量。我关心的是,在 RAM 受限的情况下,我们如何才能最大限度地提高 find 对大型集合的操作速度。

我们的搜索将趋于聚集;大约 50,000 个元素将满足大约 50% 的查询,但其余 50% 将随机分布在所有数据中。我们是否可以通过将这 50% 的数据移到他们自己的集合中来获得性能提升,以便将最常用数据的较小索引始终保留在 ram 中?

将 _id 字段的大小从 20 字节减少到 8 字节是否会对 MnogoDB 的索引速度产生重大影响?

【问题讨论】:

听起来您的文档比 RAM 多得多,所以我会尽可能地缩小文档以增加 RAM 中可以容纳的数据量。例如,确保字段名称只有一两个字符。你打算分片吗?将数据移动到同一服务器上的不同集合中不会改变 RAM 的使用——因为它无论如何都是由操作系统管理的。 随着数据的增长,我们将进行分片。 将最常用的记录放入不同的集合只是一个想法,以便将这个较小集合的索引保留在 RAM 中并尽量防止它被换出。我认为这可能很幼稚,但我不确定为什么或为什么不。 索引的内存不是独立于工作集所需的内存进行管理的。这一切都由操作系统管理。如果索引被频繁使用(比数据更多),它应该保留在内存中。 坦率地说,如果没有更多细节,很难说清楚,但在我看来,优化 MongoDB 的大 N 查询性能并不是这里要解决的正确问题。您关于将较少使用的数据移动到单独的集合中的问题是朝着这个方向迈出的一步,但我会更进一步:将完整的数据集保存在 mongo 中,并将这些 50k 大容量记录的独立副本放在更靠近您的位置'正在处理请求。但在这一点上,我们正在了解您的一致性要求是什么……也就是“有趣的领域”。 【参考方案1】:

我想到了一些策略:

1) 为“热门”文档使用不同的集合/数据库。

如果您知道哪些文档在热集中,是的,将它们移动到单独的集合中会有所帮助。这将确保热文档共同驻留在相同的范围/页面上。它还将使这些文档的索引更有可能完全在内存中。这是因为它更小并且(完全?)被更频繁地使用。

如果热文档与其他文档随机混合,那么在加载文档时您可能不得不在 B-Tree 索引的更多叶元素中出错,因为另一个文档最近加载或访问了索引块的概率很小。

2) 缩短索引的

索引值越短,适合单个 B-Tree 块的值就越多。 (注意:键不包含在索引中。)单个存储桶中的条目越多,意味着存储桶越少,索引所需的总内存就越少。这意味着块将保留在内存中的概率更高/寿命更长。在您的示例中,减少 20->8 个字符比节省 50% 好。如果您可以将这 8 个字节转换为 long,则可以节省更多,因为 long 没有长度前缀(4 个字节)和尾随 null(总共 5 个字节)。

3) 缩短键名。

字段名称越短,每个文档占用的空间就越少。 这具有降低可读性的不幸副作用。

4) 分片

面对整个语料库的读取会耗尽内存和最终的磁盘带宽,这确实是保持性能提高的唯一方法。如果您进行分片,您仍然需要对“热”集合进行分片。

5)Adjust the read-ahead on disk to a small value.

由于“非热”读取是从磁盘加载随机文档,我们真的只想将该文档以及尽可能少的文档读取/故障到内存中。一旦用户从文件的一部分中读取数据,大多数系统都会尝试并预读一大块数据。这与我们想要的完全相反。

如果您发现系统出现很多故障,但 mongod 进程的常驻内存未接近系统可用内存,您可能会看到操作系统读取无用数据的影响。

6) 尝试对键使用单调递增的值。

这将触发优化(针对基于 ObjectId 的索引),当索引块拆分时,它将以 90/10 而不是 50/50 执行此操作。结果是索引中的大多数块将接近容量,您将需要更少的块。

如果您事后只知道“热”的 50,000 个文档,那么将它们按索引顺序添加到单独的集合中也会触发此优化。

罗伯。

【讨论】:

以上是关于在 MongoDB 中快速搜索数十亿个小文档的策略的主要内容,如果未能解决你的问题,请参考以下文章

在庞大的 mongodb 集合中快速搜索非常稀有的字段

如何存储数十亿 JSON 文件并进行查询

将 s3 中跨 CSV 文件的数十亿条记录推送到 MongoDb

Firebase javascript安全? [复制]

Elasticsearch:索引数据是如何完成的

Elasticsearch:索引数据是如何完成的