MongoDB查询异常缓慢。

4

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

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

这个查询非常慢,虽然没有建立索引,但是根据我的MySQL经验,执行这样的查询不应该需要20分钟

在mongo终端上直接执行时,CPU使用率仅为3%,而仍有超过2GB的可用内存。

感谢您给了我一些提示!

编辑:查询的explain()(不包括count)结果如下:

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
                    ]
            ]
    }
}

1
解释查询总是一个良好的第一步。 - shx2
顺便提一下,在写类似“对于计数,Mongo必须遍历所有元组”的内容之前,请考虑一下我们仅针对50万条记录进行15分钟的讨论。 :) - Doidel
我只能找到一个“explain()”,没有“describe”。而且那个“explain”现在运行了1分钟。CPU和内存使用率都很低。 - Doidel
explain() 还没有终止。一旦它终止,我会在这里写下来 ^^ - Doidel
1
磁盘IO可能受限。iostat显示什么? - Mzzl
显示剩余5条评论
2个回答

6
TL;DR: 反转逻辑:在新字段has_cedd上添加一个稀疏索引(具有低选择性但通过稀疏性得到改进),该字段为null或某个常量,或者更好的做法是,在其他地方保留一个全局计数器,每次写操作都会更新它。
如果featureData.cedd最大可包含5k个字符,则对其进行索引似乎不是一个好主意,因为这远远超出了最大索引大小,显然您只关心数据本身是否存在。
哦,为什么会这么慢呢?可能是为了保持即席请求的速度。MongoDB可以将所有资源都用于此类OLAP查询,但这会导致任何“常规OLTP样式查询”产生延迟。
这里有两个问题:
1. $exists : false很危险,我怀疑索引也没有帮助:索引是为数据而设计的,而$exists是结构上的“元查询”。如果字段上有一个索引并且查询是$exists : true,它可以使用索引,因为如果已存在索引值,则给定文档中必须也存在该字段本身。反转这种逻辑很棘手:如果该字段不存在,则它不在索引中或其选择性非常低。顺便说一下,使用$ne的查询也是如此,反转索引通常存在问题。
2. MongoDB将必须对500k个对象进行反序列化并检查每一个对象以执行$exists。您不能将其与具有固定表结构的MySQL进行比较,事实上,$exists : false没有SQL等效项,因为该字段必须存在,否则您的表格会出现严重错误。

感谢您的解释!但我仍然想知道为什么 OLAP 查询只使用了 3% 的 CPU?我能否以某种方式增加它呢?既然数据库上没有运行其他程序,这将是一种极大的资源浪费... - Doidel
MAYDAY 添加索引,将查询时间从20分钟缩短至2毫秒!真是太神奇了!我已经在问题中添加了新的 explain()。 - Doidel
所以我猜索引创建了一个单独的列表,其中没有这种要索引的功能,因此搜索只需要扫描确切数量的结果,例如在这种情况下是438个? - Doidel
1
啊,很有趣。$exists:false 寻找空值 - 只要其他对象没有 cedd:null (作为有效值)出现得太频繁,它就能高效地发挥作用,但那似乎是实际情况。这些对象可能会更小,因为 cedd 字段非常大,是吗?否则,查询速度应该只能快一千倍。你使用什么数据类型作为主键? - mnemosyn
你是否想用新的信息编辑你的帖子,这样我就可以接受它作为答案了吗? - Doidel
显示剩余2条评论

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接