【问题标题】:Mongodb query exceptionally slowmongodb查询异常慢
【发布时间】:2014-03-27 17:47:23
【问题描述】:

我的 mongodb 相当简单:一个数据集/条目在 3 层上有大约 30 个属性。一个这样的条目最多有大约 5000 个字符。我有500k。 当我执行以下查询时...

db.images.find({ "featureData.cedd": { $exists: false}}).count()

...它非常慢。它没有被索引,但仍然......根据我的 MySQL 经验,执行一个这样的查询不应该花费 20 分钟

在执行时(直接在 mongo 终端上)有 3% 的 CPU 使用率和超过 2 Gigs 的可用内存。

感谢您提示我可以做什么!

编辑:查询的解释()(不计)给出:

db.images.find({ "featureData.cedd": { $exists: false }}).explain()
{
    "cursor" : "BasicCursor",
    "nscanned" : 532537,
    "nscannedObjects" : 532537,
    "n" : 438,
    "millis" : 1170403,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {

    }
}

iostat 的输出:

Linux 3.2.0-58-generic (campartex)      03/25/2014      _x86_64_        (2 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          34.93    0.01    0.25    0.48    0.00   64.33

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda               2.08       103.79        11.26  172805914   18749067
fd0               0.00         0.00         0.00        148          0

explain() 添加索引后的输出

db.images.find({ "featureData.cedd": { $exists: false }}).explain()
{
    "cursor" : "BtreeCursor featureData.cedd_1",
    "nscanned" : 438,
    "nscannedObjects" : 438,
    "n" : 438,
    "millis" : 2,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : true,
    "indexOnly" : false,
    "indexBounds" : {
            "featureData.cedd" : [
                    [
                            null,
                            null
                    ]
            ]
    }
}

【问题讨论】:

  • explain查询总是一个好的第一步
  • 顺便说一句,在写诸如“计算 mongo 必须遍历所有元组”之类的东西之前,请考虑一下,我们在这里谈论 15 分钟,只有 50 万条记录。:)
  • 我只能找到一个“explain()”,没有描述。 “解释”现在运行了 1 分钟 x/ CPU 和内存使用率一样低
  • explain() 尚未终止。有的话我就写到这里^^
  • 磁盘 IO 可能受限。 iostat 是怎么说的?

标签: mongodb


【解决方案1】:

TL;DR:反转逻辑:在新字段 has_cedd 上添加一个稀疏索引,该索引要么为 null,要么为某个常量(低选择性索引,不理想,但通过稀疏改进),或者更好的是,保持全局每次写入操作都会更新的其他地方的计数器。

如果 featureData.cedd 最多可以包含 5k 个字符,那么索引 featureData.cedd 听起来是个坏主意,因为这远远超出了最大索引大小,而且您显然对数据本身不感兴趣,只对它是否存在感兴趣。

哦,为什么这么慢?可能是为了快速保持临时请求。 MongoDB 可以将所有资源专用于这种类似 OLAP 的查询,但这会导致任何“常规 OLTP 样式查询”的延迟。


这里有两个问题:

  1. $exists : false 是邪恶的,我怀疑索引是否会有所帮助:索引是为 data 创建的,而 $exists 是结构上的“元查询”。如果字段上有索引并且查询是$exists : true,它可以使用索引,因为如果存在索引值,则字段本身也必须存在于给定文档上。颠倒这个逻辑很棘手:如果该字段不存在,则它不在索引中,或者它的选择性非常低。 “转身”索引通常是有问题的,顺便说一下,对于使用 $ne 的查询也是如此。

  2. MongoDB 必须反序列化 500k 个对象并检查每个对象以执行$exists。您无法将其与具有固定表结构的 MySQL 进行比较,事实上,$exists : false 没有 SQL 等效项,因为该字段必须存在,否则您的表将严重损坏。

    李>

【讨论】:

  • 感谢您的解释!我仍然想知道为什么只有 3% 的 CPU 用于 OLAP 查询?我可以以某种方式增加它吗?由于数据库上没有运行其他任何东西,这会浪费资源......
  • MAYDAY 添加索引将其从 20 分钟缩短到 2 毫秒!哇!我将新的 explain() 添加为问题的编辑
  • Sooo 我猜索引创建了一个单独的列表,其中包含那些不具有索引功能的列表,因此搜索只需扫描确切数量的结果,在这种情况下为 438?
  • 啊,有趣。 $exists:false 查找空值 - 只有在其他对象不经常具有 cedd:null 时(作为有效值)才能有效工作,但情况似乎如此。这些对象可能要小得多,因为cedd 字段太大了,对吗?否则,查询应该“只”快一千倍。您使用什么数据类型作为主键?
  • int。但是在这种情况下我不通过id查询,只是说。但是,是的,那些没有 cedd 功能的往往要小得多。我也对这个因素感到惊讶,它提高了 600'000 倍^^
【解决方案2】:

在我的例子中,添加索引将查询速度提高了 600'000 倍。 $exists:false 查找空值 - 只有当其他对象不经常具有 cedd:null 时(作为有效值)才能有效地工作。这里就是这种情况。此外,没有 cedd 值的对象要小得多。

【讨论】:

    猜你喜欢
    • 2015-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多