【问题标题】:Querying and sorting a large amount of data in mongo, by fields which might not exist根据可能不存在的字段查询和排序mongo中的大量数据
【发布时间】:2019-12-05 20:14:40
【问题描述】:

我对 mongo 比较陌生,我有一个如下所示的集合:

[
    {
        "stored": {
            "box": [
                {
                    "parcelId": "uwb1",
                    "status": "ACTIVE"
                }
            ]
        },
        "checked": {
            "box": [
                {
                    "parcelId": "uwb1",
                    "status": "ACTIVE"
                }
            ]
        }
    },
    {
        "stored": {
            "box": [
                {
                    "parcelId": "aqrf123",
                    "status": "PENDING"
                }
            ]
        },
        "checked": {
            "box": [
                {
                    "parcelId": "aqrf123",
                    "status": "PENDING"
                }
            ]
        }
    },
    {
        "checked": {
            "box": [
                {
                    "parcelId": "zuz873",
                    "status": "ACTIVE"
                }
            ]
        }
    }
]

关于数据的一些观察:

  • 文档将始终包含 checked 字段,但可能没有 stored 字段
  • checkedstored 字段具有相同的架构
  • 两者都将始终具有 box 字段,我们可以假设 box 字段将始终在数组中具有 1 个元素(只有 1 个,不多也不少)
  • 此集合中的文档数量相对较高(约 1 亿)

我想要实现的目标是让文档按 status 字段排序,这就像一个枚举,它可以有 3 个值 - ACTIVEPENDINGREJECTED

  • 如果对于文档,stored 字段存在,我将从那里获取它并忽略 checked 字段。
  • 否则我将不得不从checked 字段中获取它,如前所述,该字段保证存在。
  • 一个重要的要求是将整个文档返回给消费者/客户端,所以我不能使用projection 来减少来自文档的数据量(这可能会使整个操作更快)。

我如何尝试实现这是通过使用如下所示的聚合:

db.getCollection('entries')
    .aggregate([{
            $addFields: {
                sortStatus: {
                    $ifNull: [{
                        $let: {
                            vars: {
                                box: {
                                    $arrayElemAt: [
                                        "$stored.box", 0
                                    ]
                                }
                            },
                            in: "$$box.status"
                        }
                    }, {
                        $let: {
                            vars: {
                                box: {
                                    $arrayElemAt: [
                                        "$checked.box", 0
                                    ]
                                }
                            },
                            in: "$$box.status"
                        }
                    }]
                }
            }
        },
        {
            $sort: {
                sortStatus: 1
            }
        }
    ], {
        allowDiskUse: true
    })

这似乎可以完成这项工作,但感觉很慢。还有allowDiskUse,这让我有点不舒服。如果我忽略它,我会收到 Sort exceeded memory limit of x bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in 错误消息。

所以我的问题是:

  1. 是否有更快的替代方案,不管是聚合还是不聚合?
  2. 在进行聚合时使用allowDiskUse 选项是否有任何风险?
  3. 改变一点文档结构并将该可排序字段添加到文档的根目录,为其添加索引并使用.sort({"statusField": 1}) 会更好(或者它是“mongo”方式)吗?这将是最后的选择,因为我必须迁移现有数据。

【问题讨论】:

    标签: mongodb mongodb-query projection


    【解决方案1】:

    您的sortStatus 字段值可以通过以下方式获得:

    { $addFields: { sortStatus: { $ifNull: [ "$stored.box.status", "$checked.box.status" ] } } },
    

    这会使查询更快吗?没有,但代码更简单。


    (1) 是否有更快的替代方案,无论是聚合还是不聚合?

    我不知道,现在。


    (2) 使用 allowDiskUse 选项是否存在风险? 聚合?

    使用allowDiskUse:true 选项意味着当此操作的内存 (RAM) 超过其限制时,排序操作会使用磁盘获取额外资源。与内存相比,磁盘 IO非常慢,因此“风险”是慢得多的排序操作。当排序操作需要的内存超过 100MB 的限制时,此选项成为强制性的(请参阅Sort and Memory Restrictions in Aggregation 上的文档)。


    (3) 稍微改变一下会更好(或者是“mongo”方式) 文档结构并将该可排序字段添加到 文档,为其添加一个索引,然后使用 .sort({"statusField": 1})? 这将是最后的选择,因为我必须迁移 现有数据。

    创建一个新的状态字段并在该字段上建立一个索引意味着新的考虑:

    • 创建新字段“状态”需要在 编写文档的时间(也可能在更新期间)。
    • 在此新字段上创建索引也是写入期间的额外开销。请注意,索引大小会随着文档数量的增加而变大。

    这些会影响应用程序的写入性能。

    但是,查询将变成一个简单的排序。在集合中有大量文档的情况下,用于排序的索引在操作期间可能适合内存,也可能不适合内存。如果没有一些实际试验,您无法确定此选项如何提供帮助。

    这是Indexing Strategies 上的一些文档。

    【讨论】:

    • 我明白了,谢谢你的详细回答。由于allowDiskUse 而变慢并不是什么大问题,我担心的是不会引起任何问题/致命错误。确实,选项(3)似乎更让人头疼。
    猜你喜欢
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 1970-01-01
    • 2019-09-01
    • 2020-03-08
    相关资源
    最近更新 更多