【问题标题】:Why mongodb sort by _id is much faster than sort by any other indexed field?为什么按_id排序的mongodb比按任何其他索引字段排序要快得多?
【发布时间】:2018-09-20 10:48:50
【问题描述】:

我正在尝试按单个字段对包含数百万行的集合进行完全排序。 据我所知,ObjectId 包含 4 个字节的时间戳。我的时间戳是 4 字节整数索引字段。所以我想按 _id 和时间戳排序应该是相似的,但这里是结果

db.coll.find().sort("_id", pymongo.ASCENDING)
# takes 25 minutes to run

db.coll.find().sort("timestamp", pymongo.ASCENDING)
# takes 2 hours to run

为什么会发生这种情况,这是优化它的方法吗? 谢谢

更新

我尝试排序的时间戳字段已按我的指示编入索引

收集统计数据

"size" : 55881082188,
"count" : 126048972,
"avgObjSize" : 443,
"storageSize" : 16998031360,
"capped" : false,
"nindexes" : 2,
"totalIndexSize" : 2439606272,

我致力于 mongodb 进程 4gb 的 ram(试图增加到 8gb 但速度没有增加)

更新 2

事实证明有多少字段顺序遵循插入(自然)顺序,所以排序速度更快

我试过了

db.new_coll.create_index([("timestamp", pymongo.ASCENDING)])
for el in db.coll.find().sort("timestamp", pymongo.ASCENDING):
    del el['_id']
    db.new_coll.insert(el)

# and now
db.new_coll.find().sort("timestamp", pymongo.ASCENDING)
# takes 25 minutes vs 2 hours as in previous example

【问题讨论】:

  • 你使用的时间戳字段是什么格式的?
  • @PardeepSingh 4 字节 Unix 时间(如 1523386147)
  • 说真的,即使您在_id 上进行排序也需要相当长的时间。我假设要么我们的 RAM 有问题,要么我们正在谈论一个分片集群。请在您的问题by editing it 中添加有关您的 RAM 利用率和设置的信息,包括数据库的大小(大小,不仅仅是条目数!)。旁注:MongoDB 中没有行的概念。越早停止对 MongoDB 应用 SQL 思维方式越好。

标签: mongodb mongodb-query pymongo


【解决方案1】:

索引。

当您使用 MongoDB sort() 方法时,您可以为结果集指定排序顺序 - 升序 (1) 或降序 (-1)。如果不为排序字段建立索引,MongoDB 将在查询时对结果进行排序。在查询时排序会使用 CPU 资源并延迟对应用程序的响应。但是,当索引包含用于选择并按正确顺序对结果集进行排序的所有字段时,MongoDB 不需要在查询时进行排序。相反,结果已经在索引中排序,并且可以立即返回。

请在此处查看更多详细信息。 https://mobile.developer.com/db/indexing-tips-for-improving-your-mongodb-performance.html

https://docs.mongodb.com/manual/tutorial/sort-results-with-indexes/

【讨论】:

    【解决方案2】:

    由于 _id 字段值的生成方式,按 _id 排序更快。

    Documentation的话

    ObjectId 以时尚方式生成的主要原因之一 驱动程序上面提到的是它包含一个有用的行为 由于排序的工作方式。鉴于它包含一个 4 字节 时间戳(秒分辨率)和递增计数器 因为可以使用一些更唯一的标识符,例如机器 ID _id 字段按创建顺序对文档进行排序 只需对 _id 字段进行排序。这对于节省空间很有用 如果您希望跟踪 创建文档。

    我也尝试解释查询,并注意到当使用 _id 进行排序时,nscannedObjects 和 nscannedObjectsAllPlans 为 0。

    > db.coll.find({},{_id:1}).sort({_id:1}).explain();
    {
            "cursor" : "BtreeCursor _id_",
            "isMultiKey" : false,
            "n" : 353,
            "nscannedObjects" : 0,
            "nscanned" : 353,
            "nscannedObjectsAllPlans" : 0,
            "nscannedAllPlans" : 353,
            "scanAndOrder" : false,
            "indexOnly" : true,
            "nYields" : 2,
            "nChunkSkips" : 0,
            "millis" : 0,
            "indexBounds" : {
                    "_id" : [
                            [
                                    {
                                            "$minElement" : 1
                                    },
                                    {
                                            "$maxElement" : 1
                                    }
                            ]
                    ]
            },
            "server" : "server",
            "filterSet" : false
    }
    

    【讨论】:

    • 您没有在解释输出中看到扫描的任何文档,因为您正在解释一个covered query,其中查询投影中的所有字段(在您的情况下为{_id:1})都可以使用回答完全使用索引。这与_id 值的格式无关。
    【解决方案3】:

    _id 字段是自动创建的,该字段在将文档插入 MongoDB 数据库集合时存储一个 12 字节的 ObjectId 值,该值表示属于集合的 BSON 文档的唯一值。

    根据 MongoDB 的文档

    The 12-byte ObjectId value consists of:
    
    a 4-byte value representing the seconds since the Unix epoch,
    a 3-byte machine identifier,
    a 2-byte process id, and
    a 3-byte counter, starting with a random value.
    

    在集合字段上定义的索引加快了存储到数据库集合中的数据的检索过程,因为属于索引字段的值按特定的排序顺序排序,一旦找到匹配的值就会停止文档扫描,从而最大限度地减少要扫描的文档数量。

    在创建集合期间在 _id 字段上定义唯一索引,因此按 _id 字段对数据进行排序有助于从集合中快速检索数据。

    【讨论】:

      猜你喜欢
      • 2011-11-26
      • 1970-01-01
      • 2011-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多