我正在使用MongoDB(Atlas)将约5500万个时序文件保存在数据库中
这些文档通常如下所示:
{
"_id" : ObjectId("5c8f1fb127fcc613f422677d"),
"capture_time" : ISODate("2019-03-18T04:33:51Z"),
"key" : "9727eec91/51c922a.png",
"e_id" : 20702
}
并非所有文档都具有“ e_id”,因此我已将“ e_id”编入索引,如下所示:
{
"v" : 2,
"key" : {
"e_id" : 1
},
"name" : "e_id_1",
"ns" : "events.detection_events",
"partialFilterExpression" : {
"e_id" : {
"$exists" : true
}
}
}
如您所见,我在“ e_id”上有部分索引。
运行时db.coll.count({"e_id": {$exists:true}})
,响应超时,并且收到有关磁盘IO达到90%的警告
我跑步时也会发生同样的事情db.coll.distinct("e_id", {"e_id": {$exists:true}})
。
如果我运行,db.coll.find({"e_id": {$exists:true}})
它将运行得很快。
我在这个集合中是否有太多的文档,无法根据“ e_id”对它们进行计数/区分?
编辑
这是db.coll.find({“ e_id”:{$ exists:true}})查询中的.explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "db.coll",
"indexFilterSet" : false,
"parsedQuery" : {
"e_id" : {
"$exists" : true
}
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"e_id" : {
"$exists" : true
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"e_id" : 1
},
"indexName" : "emp_id_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"e_id" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : true,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"e_id" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "cluster0.mongodb.net",
"port" : 29017,
"version" : "4.0.12",
"gitVersion" : "5776e76dbf9e7afe86e6b39g22520ffb6766e95d4"
},
"ok" : 1,
"operationTime" : Timestamp(1571177083, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1571177083, 2),
"signature" : {
"hash" : BinData(0,"fvD6+eR72y83kO15Tk3TAmZtn5I="),
"keyId" : NumberLong("6728051106256797217")
}
}
}
索引是良好查询性能的重要组成部分。这也适用于聚合查询。
通常,查询性能取决于各种因素,包括数据量,文档大小,索引,查询的构建方式以及诸如处理器,硬盘驱动器,RAM和网络等硬件考虑因素。如果服务器上还有其他负载,它也会影响您当前正在运行的查询。
查询速度慢?首先检查集合上是否定义了任何索引。仍然很慢,请使用explain()运行查询计划。使用选项/模式“ executionStats”。查询计划文档将显示获胜的查询计划以及该计划的执行统计信息。计划文档显示是否使用了索引(IXSCAN)或集合扫描(COLLSCAN)和阶段。如果使用索引,则使用什么索引。执行统计信息显示不同阶段返回的文档数,执行时间,检查的索引键总数和检查的文档等。
在上述问题场景中,集合中的文档数量很重要(50+百万)。如果在大量键上创建索引,它将是大型索引;这可能很难加载到内存中。如果索引不能在内存中,则会有磁盘使用情况,并且查询速度会很慢。
查询,第一个返回计数,第二个返回字段的不同值eid
:
db.time_series.count( { eid: { $exists : true } } );
db.coll.distinct( eid, { eid: {$exists: true} } )
我使用120万个文档的相似数据样本尝试了上述查询。我运行了带有统计信息的查询计划,以用于计数和不同的查询。在执行统计是我的机器上,如下所示:
"nReturned" : 0,
"executionTimeMillis" : 778,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1145477,
"nReturned" : 1144845,
"executionTimeMillis" : 775,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1145477,
就像在问题中一样,我在字段上创建了部分索引eid
。
db.time_series.createIndex( { eid: 1 }, { partialFilterExpression: { eid: { $exists: true } } } )
db.time_series.getIndexes() shows the newly created index:
[
...
{
"v" : 2,
"key" : {
"eid" : 1
},
"name" : "eid_1",
"ns" : "test.time_series",
"partialFilterExpression" : {
"eid" : {
"$exists" : true
}
}
}
]
该查询计划显示,使用了索引。统计数据:
"nReturned" : 0,
"executionTimeMillis" : 4278,
"totalKeysExamined" : 1144845,
"totalDocsExamined" : 1144845,
"nReturned" : 1144845,
"executionTimeMillis" : 4409,
"totalKeysExamined" : 1144845,
"totalDocsExamined" : 1144845,
该executionTimeMillis
显示是该指数创建后的高得多。
另外,请注意使用了索引(IXSCAN有查询计划程序阶段)。但是,事实证明这没有用。检查的索引键数量很大,并且与返回的文档相同。
聚合查询允许分阶段处理数据。这意味着您可以对查询及其处理方式进行更多控制。同样,聚合查询可以使用类似于查找查询中的索引,并且几乎没有什么不同。一旦管道到达某些阶段,索引将不被使用;因此,请确保首先使用适当的阶段来利用索引(有时查询优化器可能会这样做)。这些可以在查询计划中注明。
聚合管道的不同和计数查询。
db.time_series.aggregate( [
{ $match: { eid: { $exists: true } } },
{ $group : { _id : "$eid" } },
{ $project: { eid: "$_id", _id: 0 } }
] )
db.time_series.aggregate( [
{ $match: { eid: { $exists: true } } },
{ $group : { _id : null, count: { $sum: 1 } } } ,
{ $project: { _id: 0 } }
] )
同样在这种情况下,我尝试使用和不使用eid
字段上的索引。可以在聚合查询中使用以下说明:
db.time_series.explain("executionStats").aggregate( [ ... ] )
有和没有索引的解释统计:
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1144845,
"executionTimeMillis" : 4813,
"totalKeysExamined" : 1144845,
"totalDocsExamined" : 1144845,
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1144845,
"executionTimeMillis" : 8322,
"totalKeysExamined" : 1144845,
"totalDocsExamined" : 1144845,
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1144845,
"executionTimeMillis" : 1043,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1145477,
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1144845,
"executionTimeMillis" : 3884,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1145477,
关于聚合中的内存约束的说明:
{allowDiskUse : true }
选项与聚合查询一起使用。这应该是最后的选择,因为使用此选项时性能会降低;这是可以预期的,因为硬件访问要慢得多,因为工作会溢出到磁盘上。这在批处理中更常用。实现正确的查询性能主要是一门科学。为此有很多工具,例如解释/查询计划器和索引。还需要对示例文档进行一些选项的反复试验。这也是一门数学。文档的大小和文档的数量表明文档和索引使用多少内存字节。
参考文献:
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句