【问题标题】:query performance while filtering embedding large arrays documents Mongodb过滤嵌入大型数组文档时的查询性能 Mongodb
【发布时间】:2018-03-25 23:44:00
【问题描述】:

在我的 mongodb 集合中,我有 1500 万个具有以下 json 结构的文档。每个 json 文档的 playfields 数组字段中的嵌入文档计数都会发生变化。我所有的查询都涉及根据 playfields 数组字段中的数据过滤文档。所有查询的执行时间都超过 2 分钟。

嵌入文档中的 value 字段存储多种数据类型(int、string)。这是糟糕的设计吗?

我在编写查询时做错了吗?我是否缺少任何索引?我是否必须将数据从单个文档中的嵌入文档移动到多个集合?

多条件查询(已发布)需要 3 分钟才能执行。过滤同一集合时使用的语法是否错误?我的目标是返回满足所有这些条件的文档。

如果我将查询分成几部分,则每个都需要 ms 才能执行。 1) db.playfieldvalues.find({$or:[ {playfields: {$elemMatch:{ID:"Play.NHL.NHLAwayTeam" ,value: "NYI NEW YORK ISLANDERS"}}},{playfields: {$elemMatch: {ID:“Play.NHL.NHLAwayTeam”,值:“TB TAMPA BAY LIGHTNING”}}}]}) 2) db.playfieldvalues.find({playfields: {$elemMatch:{ID:"Play.NHL.NHLHomeTeam" ,value: "BOS BOSTON BRUINS"}}}) 3) db.playfieldvalues.find({playfields: {$elemMatch:{ID:"Play.NHL.NHLEventX" ,value: {$gt: 0, $lt: 25}}}}) 4) db.playfieldvalues.find({playfields: {$elemMatch:{ID:"Play.NHL.NHLEventScoreDifferential" ,value: {$gt: 0}}}})

已创建索引:

db.collection.ensureIndex({ "playfields.ID": 1, "playfields.value": 1 })

正在运行的查询:

1:

db.playfieldvalues.find({playfields: {$elemMatch:{ID:"Play.NHL.NHLHomeTeam" ,value: "BOS BOSTON BRUINS"}}})

2:

db.playfieldvalues.find({$and:[
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLHomeTeam" ,value: "BOS BOSTON BRUINS"}}},
  {$or:[ {playfields: {$elemMatch:{ID:"Play.NHL.NHLAwayTeam" ,value: "NYI NEW YORK ISLANDERS"}}},{playfields: {$elemMatch:{ID:"Play.NHL.NHLAwayTeam" ,value: "T.B TAMPA BAY LIGHTNING"}}}]},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLEventY" ,value: -38}}},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLEventX" ,value: {$gt: 0}}}},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLEventScoreDifferential" ,value: {$gt: 0}}}}
  ]
})

JSON 文档示例:

{ 
    "_id" : ObjectId("59dbd4c5704aa82e70ac10b5"), 
    "playid" : "2594c658-aa3b-4a98-b2eb-0cc03e4dc9e5", 
    "playfields" : [
        {
            "ID" : "Play.NHL.NHLGameDate", 
            "TS" : "", 
            "value" : NumberInt(20160228)
        }, 
        {
            "ID" : "Play.GameDate", 
            "TS" : "", 
            "value" : "2/28/2016 12:00:00 AM"
        }, 
        {
            "ID" : "Play.NHL.NHLEventType", 
            "TS" : "", 
            "value" : "HIT"
        }, 
        {
            "ID" : "Play.NHL.NHLClockTime", 
            "TS" : "", 
            "value" : "03:08"
        }, 
        {
            "ID" : "Play.NHL.NHLClockTimeSeconds", 
            "TS" : "", 
            "value" : NumberInt(188)
        }, 
        {
            "ID" : "Play.NHL.NHLEventX", 
            "TS" : "", 
            "value" : NumberInt(62)
        }, 
        {
            "ID" : "Play.NHL.NHLEventY", 
            "TS" : "", 
            "value" : NumberInt(-38)
        }, 
        {
            "ID" : "Play.NHL.NHLEventPeriod", 
            "TS" : "", 
            "value" : "1"
        }, 
        {
            "ID" : "Play.NHL.NHLGameCode", 
            "TS" : "", 
            "value" : "20933"
        }, 
        {
            "ID" : "Play.NHL.NHLSeason", 
            "TS" : "", 
            "value" : "20152016"
        }, 
        {
            "ID" : "Play.NHL.NHLHomeTeam", 
            "TS" : "", 
            "value" : "BOS BOSTON BRUINS"
        }, 
        {
            "ID" : "Play.NHL.NHLAwayTeam", 
            "TS" : "", 
            "value" : "T.B TAMPA BAY LIGHTNING"
        }, 
        {
            "ID" : "Play.NHL.NHLPrimaryTeam", 
            "TS" : "", 
            "value" : "T.B TAMPA BAY LIGHTNING"
        }, 
        {
            "ID" : "Play.NHL.NHLPrimaryTeamActionPlayer", 
            "TS" : "", 
            "value" : "e27ca5e6-d4fa-4d45-8fa2-a860f64f7ea7"
        }, 
        {
            "ID" : "Play.NHL.NHLSecondaryTeam", 
            "TS" : "", 
            "value" : "BOS BOSTON BRUINS"
        }, 
        {
            "ID" : "Play.NHL.NHLSecondaryTeamActionPlayer", 
            "TS" : "", 
            "value" : "bea1deb6-aabd-47e8-b216-6f4df5f1ea97"
        }, 
        {
            "ID" : "Play.NHL.NHLEventZone", 
            "TS" : "", 
            "value" : "DZ"
        }, 
        {
            "ID" : "Play.NHL.NHLEventScoreDifferential", 
            "TS" : "", 
            "value" : NumberInt(1)
        }, 
        {
            "ID" : "Play.NHL.NHLEventStrength", 
            "TS" : "", 
            "value" : "Even"
        }
    ]
}

附加第二个查询的解释输出:

【问题讨论】:

  • 我是第一次使用 Mongodb。我正在阅读有关索引和尝试的文章,但没有突破。任何人都可以就这个问题给我建议。
  • 您使用的是哪个版本的 MongoDB?您使用的是 WiredTiger 存储引擎吗?另外我会在查询末尾添加 .explain() 。示例:db.playfieldvalues.find({playfields: {$elemMatch:{ID:"Play.NHL.NHLHomeTeam" ,value: "BOS BOSTON BRUINS"}}}).explain();如果获胜计划阶段是 FETCH,IXSCAN 则意味着它使用您的索引。你也可以在这里查看文档docs.mongodb.com/manual/reference/explain-results
  • mongodb 版本:3.4.9 WiredTiger 存储引擎:不知道。如何检查。我下载了 mongodb 安装程序并将其安装在 windows server 2012 操作系统上。我运行了解释功能,我不知道如何解释它。将发布有问题的解释结果
  • 如何衡量执行时间?您的客户端和服务器之间是否存在慢速网络?
  • 第二次查询的执行时间 4.6 分钟。我直接在服务器上运行。没有网络问题。

标签: json mongodb nosql nosql-aggregation


【解决方案1】:

嵌入文档中的 value 字段存储多种数据类型(int、 细绳)。这是糟糕的设计吗?

如果您亲自问我,来自 java 背景,是的,这是一个糟糕的设计。但是对于 MongoDB,这并不是一个真正的问题,只要您的应用程序可以处理它。仅供参考,您的索引大小也应该很大,您可以通过db.playfieldvalues.stats() 进行检查,但这也无关紧要。至少它与您的性能问题无关。

我在编写查询时做错了吗?

嗯,查询有点复杂,需要满足很多条件。 您还可以使用 $or 运算符将查询更改为

db.playfieldvalues.find({
$and:[
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLHomeTeam" ,value: "BOS BOSTON BRUINS"}}},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLAwayTeam" ,value: {$in: ["NYI NEW YORK ISLANDERS", "T.B TAMPA BAY LIGHTNING"]}}}},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLEventY" ,value: -38}}},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLEventX" ,value: {$gt: 0}}}},
  {playfields: {$elemMatch:{ID:"Play.NHL.NHLEventScoreDifferential" ,value: {$gt: 0}}}}
  ]
})

尽量简化您的查询参数。也许甚至与.limit(n).skip(n) 合作对您来说是一个更好的解决方案。

我是否缺少任何索引?

看看这篇文章,虽然它可能是相关的。 Slow range query on a multikey index

但在继续删除并创建新索引之前,请尝试删除查询的某些部分以尝试找出可能导致它的确切原因。例如,我会删除这些行:

{playfields: {$elemMatch:{ID:"Play.NHL.NHLEventX" ,value: {$gt: 0}}}},
{playfields: {$elemMatch:{ID:"Play.NHL.NHLEventScoreDifferential"
,value: {$gt: 0}}}}

并检查性能是否仍然相同。查看文档 docs.mongodb.com/manual/core/multikey-index-bounds 。查看您的 indexBounds -> playfields.ID 和 playfields.value。我不确定为什么 playfields.value: (0.0, 25.0)。在文档中,它说评级的界限:{ $gte: 0 } 谓词是 [ [ 0, Infinity ] ]

我是否必须将数据从单个文档中的嵌入文档移动到 多个集合?

不,您不必这样做。没有固定的模式,你可以随心所欲。

关于如何检查您是否使用 wiredTiger 的评论中的问题:db.serverStatus().storageEngine。但是由于您使用的是 mongo 3.4.9 版,因此您使用的是 wiredTiger

【讨论】:

  • 我删除了值字段的范围搜索替换为等号。并运行解释功能。现在执行时间是 10.385 秒。检查的文档总数:16036,nreturned:2。以前的执行时间更高(旧的解释输出发布在问题部分)。我关注了您的链接,他们推荐的范围搜索解决方案是按顺序使用 $lt,$gt。就我而言,我只使用了 $gt。
  • 所以 $gt 让它变慢了,对吗?另请查看文档docs.mongodb.com/manual/core/multikey-index-bounds。查看您的 indexBounds -> playfields.ID 和 playfields.value。我不确定为什么 playfields.value: (0.0, 25.0)。在文档中,它说评级的界限:{ $gte: 0 } 谓词是 [ [ 0, Infinity ] ]。这似乎也很相关:stackoverflow.com/questions/28361904/…。尝试在您的范围查询中添加 $lt 或 $lte。这有帮助吗?
  • 我添加了范围搜索($lt 和 $gt),解释函数输出显示执行时间:7 秒。与只有 $gt 运算符相比要好得多。通过范围搜索,检查的文档为 48,520。没有范围搜索(仅 $gt),它检查了 206 K。但在现实世界中仍然需要 7 秒来收集 1300 万份文档。
  • 以下查询无字符串搜索需要 3 分钟的执行时间。此查询也不涉及任何范围搜索。 db.playfieldvalues.find({$and:[ {playfields: {$elemMatch:{ID:"Play.NHL.NHLHomeTeam" ,value: "BOS BOSTON BRUINS"}}}, {$or:[ {playfields: {$ elemMatch:{ID:"Play.NHL.NHLAwayTeam" ,value: "NYI NEW YORK ISLANDERS"}}},{playfields: {$elemMatch:{ID:"Play.NHL.NHLAwayTeam" ,value: "TB TAMPA BAY LIGHTNING "}}}]} ] })
  • 关于 7 秒。我确实认为7秒很好。关于您的最后一条评论:使用 explain() 运行查询,这样您就可以实际查看使用了哪些索引。从粘贴在您问题上的图像中可以看出,请注意 ID [Play.NHL.NHLEventX,Play.NHL.NHLEventX] 的 indexBounds。所以它仍然不是一个仅索引查询。老实说,您的查询有点复杂。回顾一下,我会更新我的答案。
猜你喜欢
  • 1970-01-01
  • 2018-08-08
  • 2021-08-07
  • 2017-07-16
  • 2015-02-06
  • 2015-08-30
  • 2012-11-18
  • 1970-01-01
  • 2011-01-09
相关资源
最近更新 更多