【问题标题】:Mongo query take a long time. How make it more fast?Mongo查询需要很长时间。如何让它更快?
【发布时间】:2018-04-17 19:42:55
【问题描述】:

我在 node js 中使用猫鼬驱动程序。我的架构:

let sendResultSchema = mongoose.Schema({
  emailId: String,      email: String,              
  letterId: String,     sendedFrom: String,
  resultMsg: String,    owner: String,              
  created: Date,        result: Boolean,
  tag: String,          tryNum: Number,
  clickHash: String,    links: [String]
})
sendResultSchema.index({emailId: 1, letterId: 1, result: 1, owner: 1, tag: 1, clickHash: 1})
let sendResultModel = mongoose.model('sendresult', sendResultSchema)

sendresult 集合有 641000 个文档。

此查询执行 ~0.5 秒。

 db.sendresults.find({"tag" : "tagValue", "letterId" : "5ad630b5949bb02ea07d15d1"}).sort({emailId: -1}).limit(1)

我认为它必须执行得更快。你可以看到这个查询的解释here

如何让这个查询更快?

【问题讨论】:

  • sendResultSchema.index({tag: 1, letterId: 1, }); sendResultSchema.index({emailId: 1 }); 。然后阅读indexes。您不只是在每个字段上建立索引并期望它能够工作,这适用于每个数据库平台。您实际上需要将选择与您使用的查询模式相匹配。
  • @Neil 与.index({ tag: 1, letterId: 1, emailId: -1}) 相比,您的建议表现如何?求朋友... ;)
  • @john 在核心文档中有一个完整的“索引”部分,我还提供了一个链接。我建议你阅读每一页。核心是“查询”条件只能选择以查询(或排序)中存在的字段“前缀”的索引。仅仅因为索引包含字段,并不意味着它会被选中。因此,我的建议创建的索引将以所使用的字段为“前缀”。但手册中还有更多详细信息,以及其他关于一般索引的冗长博客文章和论文。
  • @john 阅读。排序是查询引擎可以使用不同索引的情况之一。
  • @john 您认为排序字段应该包含在复合索引中的印象是正确的。当排序操作需要与查询谓词完全分离的索引时,索引交集不适用:docs.mongodb.com/manual/core/index-intersection/…Optimizing MongoDB Compound Indexes 博客文章对阅读很有帮助。如果您有任何疑问,请使用explain() 确认查询计划器详细信息。

标签: mongodb mongoose


【解决方案1】:

索引需要覆盖查询的所有部分(相等部分、排序部分和范围部分)。这是因为在典型的find() 查询中,MongoDB 只使用一个索引。例如,它通常不将一个索引用于相等部分,而将另一个索引用于排序部分。

一般情况下,索引中的字段顺序需要遵循相等->排序->范围的模式。

这在Optimizing MongoDB Compound Indexes中有详细描述。

对于您的查询,相等部分为tag:..., letterId:...,排序部分为emailId:-1。您的查询中没有范围部分。

使用这个模式,你需要的复合索引是:

db.test.createIndex({tag:1, letterId:1, emailId:-1})

让我们尝试确认使用此索引可以获得多少性能提升。

测试数据

为了确认索引的适用性,我使用mgeneratejs将100万条记录插入到一​​个测试数据库中,这是一个使用模板创建随机文档的工具。

根据您的示例,我使用的 mgeneratejs 模板是:

$ cat template.json
{
  "emailId": "$hash",
  "email": "$email",
  "letterId": "$hash",
  "sendedFrom": "$email",
  "resultMsg": "$word",
  "owner": "$name",
  "created": "$date",
  "result": "$bool",
  "tag": "$word",
  "tryNum": {"$integer": {"min": 0, "max": 1e3}},
  "clickHash": "$word",
  "links": {"$array": {"of": "$url", "number": {"$integer": {"min": 1, "max": 5}}}}
}

并将 100 万个随机文档导入 MongoDB:

$ mgeneratejs template.json -n 1000000 | mongoimport -d test -c test

测试 1:非最优索引

然后我创建了您拥有的索引,并尝试查找一个不存在的文档并收集了 10 次查询运行,其中集合仅包含此索引:

> db.test.createIndex({emailId: 1, letterId: 1, result: 1, owner: 1, tag: 1, clickHash: 1})

> db.test.find({"tag" : "xyz", "letterId" : "abc"}).sort({emailId: -1}).limit(1)
Fetched 0 record(s) in 3069ms
Fetched 0 record(s) in 2924ms
Fetched 0 record(s) in 2923ms
Fetched 0 record(s) in 3013ms
Fetched 0 record(s) in 2917ms
Fetched 0 record(s) in 2961ms
Fetched 0 record(s) in 2882ms
Fetched 0 record(s) in 2870ms
Fetched 0 record(s) in 2969ms
Fetched 0 record(s) in 2863ms

因此使用该索引,查询的响应时间并不长,大多数执行时间接近 3 秒。

测试 2:相等 -> 排序 -> 范围索引

通过添加最优平等->排序->范围索引:

> db.test.createIndex({tag:1, letterId:1, emailId:-1})

> db.test.find({"tag" : "xyz", "letterId" : "abc"}).sort({emailId: -1}).limit(1)
Fetched 0 record(s) in 2ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 3ms

相比之下,使用最优索引,性能得到了显着提升。没有超过 3 毫秒的查询返回,绝大多数时间在 1 毫秒内返回。

【讨论】:

    【解决方案2】:

    如果您不需要记录中的所有字段,您可以通过只返回必要的字段来加快查询速度。 喜欢;

    db.sendresults.find({"tag" : "tagValue", "letterId" : "5ad630b5949bb02ea07d15d1"},{"_id":0,"tag":1,"email":1}).sort({emailId: -1}).limit(1)
    

    exp。使字段等于一意味着返回该字段。我制作了“_id:0”,因为我不想在 mongodb 中获取记录的 id。如果不加“_id:0”查询会自动返回id。

    在处理包含更多和嵌套字段的记录时,它为我节省了很多时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-02
      • 2018-01-09
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多