【问题标题】:Querying and sorting indexed collection in MongoDB results in data overflow在MongoDB中查询和排序索引集合导致数据溢出
【发布时间】:2014-09-01 13:43:58
【问题描述】:

“events”是一个有上限的集合,用于存储网页上的用户点击事件。文档如下所示:

{
    "event_name" : "click",
    "user_id" : "ea0b4027-05f7-4902-b133-ff810b5800e1",
    "object_type" : "ad",
    "object_id" : "ea0b4027-05f7-4902-b133-ff810b5822e5",
    "object_properties" : { "foo" : "bar" },
    "event_properties" : {"foo" : "bar" },
    "time" : ISODate("2014-05-31T22:00:43.681Z")
}

这是该集合的复合索引:

db.events.ensureIndex({object_type: 1, time: 1});

这就是我的查询方式:

db.events.find( {
   $or : [ {object_type : 'ad'}, {object_type : 'element'} ],
   time: { $gte: new Date("2013-10-01T00:00:00.000Z"), $lte: new Date("2014-09-01T00:00:00.000Z") }}, 
  { user_id: 1, event_name: 1, object_id: 1,  object_type : 1,  obj_properties : 1, time:1 } )
.sort({time: 1});

这导致:“sort() 没有索引的数据太多。添加索引或指定更小的限制” 在 mongo 2.4.9 和 “溢出排序阶段缓冲数据Mongo 2.6.3 中 33554618 字节的使用超过了 33554432 字节的内部限制”。我正在使用 Java MongoDB 驱动程序 2.12.3。当我使用“$ natural”排序时,它会引发同样的错误。似乎 MongoDB 并没有真正使用为排序定义的索引,但我不知道为什么(我阅读了关于索引的 MongoDB 文档)。我很感激任何提示。

这是 explain() 的结果:

{
    "clauses" : [
        {
            "cursor" : "BtreeCursor object_type_1_time_1",
            "isMultiKey" : false,
            "n" : 0,
            "nscannedObjects" : 0,
            "nscanned" : 0,
            "scanAndOrder" : false,
            "indexOnly" : false,
            "nChunkSkips" : 0,
            "indexBounds" : {
                "object_type" : [
                    [
                        "element",
                        "element"
                    ]
                ],
                "time" : [
                    [
                        {
                            "$minElement" : 1
                        },
                        {
                            "$maxElement" : 1
                        }
                    ]
                ]
            }
        },
        {
            "cursor" : "BtreeCursor object_type_1_time_1",
            "isMultiKey" : false,
            "n" : 399609,
            "nscannedObjects" : 399609,
            "nscanned" : 399609,
            "scanAndOrder" : false,
            "indexOnly" : false,
            "nChunkSkips" : 0,
            "indexBounds" : {
                "object_type" : [
                    [
                        "ad",
                        "ad"
                    ]
                ],
                "time" : [
                    [
                        {
                            "$minElement" : 1
                        },
                        {
                            "$maxElement" : 1
                        }
                    ]
                ]
            }
        },
    "cursor" : "QueryOptimizerCursor",
    "n" : 408440,
    "nscannedObjects" : 409686,
    "nscanned" : 409686,
    "nscannedObjectsAllPlans" : 409686,
    "nscannedAllPlans" : 409686,
    "scanAndOrder" : false,
    "nYields" : 6402,
    "nChunkSkips" : 0,
    "millis" : 2633,
    "server" : "MacBook-Pro.local:27017",
    "filterSet" : false
}

【问题讨论】:

  • 您是否尝试过仅添加时间索引,例如不是复合索引?
  • 是的,我确实为每个“时间”和“对象类型”创建了一个索引,同样的问题

标签: mongodb mongodb-query mongodb-java


【解决方案1】:

根据 explain(),当 mongo 运行查询时,它确实使用了复合索引。问题是排序({time:1})。 你的索引是{object_type:1, time:1},表示查询结果先按object_type排序,如果object_type相同,再按时间排序。

对于排序{time:1},由于顺序与索引不同({object_type:1, time:1),mongo必须将所有匹配的对象(399609)加载到内存中按时间排序})。假设对象的平均大小为 100 字节,则将超出限制。

更多信息:http://docs.mongodb.org/manual/core/index-compound/

例如,索引为 {obj_type:1, time:1} 的对象有 3 个:

{"obj_type": "a", "time" : ISODate("2014-01-31T22:00:43.681Z")}
{"obj_type": "c", "time" : ISODate("2014-02-31T22:00:43.681Z")}
{"obj_type": "b", "time" : ISODate("2014-03-31T22:00:43.681Z")}

db.events.find({}).sort({"obj_type":1, "time":1}).limit(2)

{"obj_type": "a", "time" : ISODate("2014-01-31T22:00:43.681Z")}
{"obj_type": "b", "time" : ISODate("2014-03-31T22:00:43.681Z")}

"nscanned" : 2  (This one use index order, which is sorted by {obj_type:1, time:1})

db.events.find({}).sort({"time":1}).limit(2)

{"obj_type": "a", "time" : ISODate("2014-01-31T22:00:43.681Z")}
{"obj_type": "c", "time" : ISODate("2014-02-31T22:00:43.681Z")}

"nscanned" : 3 (This one will load all the matched results and then sort)

【讨论】:

  • 当我使用终端创建索引时,我意识到 Java Mongo 驱动程序连接仍然打开,这导致了上述问题(实际上我有一个“时间”索引)。感谢您的精彩解释,我会将其标记为已接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多