【问题标题】:MongoDB Aggregation with $sample very slow带有 $sample 的 MongoDB 聚合非常慢
【发布时间】:2016-10-07 09:26:43
【问题描述】:

有很多方法可以从 mongodb 集合中选择随机文档(如 in this answer 所讨论的)。评论指出,如果 mongodb 版本 >= 3.2,那么在聚合框架中使用 $sample 是首选。但是,在包含许多小文档的集合上,这似乎非常慢。

以下代码使用 mongoengine 模拟问题,并与“skip random”方法进行比较:

import timeit
from random import randint

import mongoengine as mdb

mdb.connect("test-agg")


class ACollection(mdb.Document):
    name = mdb.StringField(unique=True)

    meta = {'indexes': ['name']}


ACollection.drop_collection()

ACollection.objects.insert([ACollection(name="Document {}".format(n)) for n in range(50000)])


def agg():
    doc = list(ACollection.objects.aggregate({"$sample": {'size': 1}}))[0]
    print(doc['name'])

def skip_random():
    n = ACollection.objects.count()
    doc = ACollection.objects.skip(randint(1, n)).limit(1)[0]
    print(doc['name'])


if __name__ == '__main__':
    print("agg took {:2.2f}s".format(timeit.timeit(agg, number=1)))
    print("skip_random took {:2.2f}s".format(timeit.timeit(skip_random, number=1)))

结果是:

Document 44551
agg took 21.89s
Document 25800
skip_random took 0.01s

过去,无论我在哪里遇到 mongodb 的性能问题,我的答案一直是使用聚合框架,所以我很惊讶 $sample 这么慢。

我在这里遗漏了什么吗?是什么导致聚合需要这么长时间?

【问题讨论】:

  • 您运行的是哪个 MongoDB 版本?我发现$sample在3.2.5中很慢,但在3.2.7中基本是瞬时的。
  • 啊,3.2.0 - 就是这样。是的,this 表明这是一个已知错误。
  • 对,但我不知道为什么在 3.2.5 和 1M 文档的新集合中它仍然很慢,因为在 3.2.3 中标记为已修复。

标签: mongodb pymongo mongoengine


【解决方案1】:

我可以确认 3.6 没有任何变化 $sample 缓慢问题仍然存在。

~40m 小文档集合,无索引,Windows Server 2012 x64。

存储: 有线Tiger.engineConfig.journalCompressor:zlib WiredTiger.collectionConfig.blockCompressor: zlib

2018-04-02T02:27:27.743-0700 我命令 [conn4] 命令 maps.places

命令:聚合 { 聚合:“地点”,管道:[ { $sample: { size: 10 } } ],

 cursor: {}, lsid: { id: UUID("0e846097-eecd-40bb-b47c-d77f1484dd7e") }, $readPreference: { mode: "secondaryPreferred" }, $db: "maps" } planSummary: MULTI_ITERATOR keysExamined:0 docsExamined:0 cursorExhausted:1 numYields:3967 nreturned:10 reslen:550 locks:{ Global: { acquireCount: { r: 7942 } }, Database: { acquireCount: { r: 3971 } }, Collection: { acquireCount: { r: 3971 } } }

protocol:op_query 72609ms

我安装 Mongo 是为了在一个严肃的项目中尝试这种“现代且高性能的 DBMS”。我有多沮丧。

解释计划在这里:

db.command('aggregate', 'places', pipeline=[{"$sample":{"size":10}}], explain=True)

 {'ok': 1.0,
  'stages': [{'$cursor': {'query': {},
    'queryPlanner': {'indexFilterSet': False,
     'namespace': 'maps.places',
     'plannerVersion': 1,
     'rejectedPlans': [],
     'winningPlan': {'stage': 'MULTI_ITERATOR'}}}},
  {'$sampleFromRandomCursor': {'size': 10}}]}

【讨论】:

    【解决方案2】:

    这是 mongodb known bug 的结果。升级to the latest version 应该可以解决这个问题。

    【讨论】:

    • 我们使用的是 MongoDB 4.2.1 。 $sample 仍然很慢。 87K 大小,收藏 5 亿张。需要 20 分钟。服务器配置为 16c/240Gb
    【解决方案3】:

    对于那些与 $sample 混淆的人,$sample 在以下情况下会很有效:

    • $sample 是流水线的第一阶段
    • N 不到集合中总文档的 5%
    • 集合包含 100 多个文档

    如果不满足上述任何条件,$sample 将执行集合扫描,然后随机排序以选择 N 文档。

    更多信息:https://docs.mongodb.com/manual/reference/operator/aggregation/sample/

    【讨论】:

      【解决方案4】:

      Mongo 声明

      如果满足以下所有条件,$sample 使用伪随机游标选择文档:

      • $sample 是流水线的第一阶段
      • N 小于集合中文档总数的 5%
      • 集合包含 100 多个文档

      如果上述任何一个条件不满足,$sample 会执行一次集合扫描,然后进行随机排序以选择 N 个文档。在这种情况下,$sample 阶段会受到排序内存限制。

      我相信你的情况 mongo 会进行全面扫描

      参考:https://docs.mongodb.com/manual/reference/operator/aggregation/sample/

      【讨论】:

        猜你喜欢
        • 2020-10-05
        • 1970-01-01
        • 1970-01-01
        • 2014-03-08
        • 2018-03-20
        • 2017-03-11
        • 1970-01-01
        • 1970-01-01
        • 2013-07-12
        相关资源
        最近更新 更多