设置详情:
mongos: RAM:8 GB,CPU:2
配置服务器(3个配置服务器的副本集): RAM:4 GB,CPU:2
Shard Cluster-1(3 mongod的副本): RAM:30 GB,CPU:4
Shard Cluster-2(3 mongod的副本): RAM:30 GB,CPU:4
拆分: 收集:rptDlp,Key:{incidentOn:" hashed"}
说明
我在一个集合中有超过1500万条记录。 我正在检索按类型日期的字段(索引一个)排序的最后一页文档。
实际查询:
db.getCollection("rptDlp").find({ incidentOn: { $gte: new Date(1513641600000), $lt: new Date(1516233600000) } })
.sort({ incidentOn: -1 }).skip(15610600).limit(10)
如果我直接针对mongo分片服务器(PRIMARY)执行此查询,则会在14秒内显示结果。但通过mongos,它需要超过2分钟,由于查询超时,我的应用程序导致显示错误提示。
如果我们将其视为网络拥塞,那么每个查询都需要2分钟。但是当我检索第一页的文档时,它会在几秒钟内显示结果。
解释查询结果(针对mongos):
{
"queryPlanner" : {
"mongosPlannerVersion" : 1,
"winningPlan" : {
"stage" : "SHARD_MERGE_SORT",
"shards" : [
{
"shardName" : "rs0",
"connectionString" : "rs0/172.18.64.47:27017,172.18.64.48:27017,172.18.64.53:27017",
"serverInfo" : {
"host" : "UemCent7x64-70",
"port" : 27017,
"version" : "3.4.10",
"gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
},
"plannerVersion" : 1,
"namespace" : "mydatabase.rptDlp",
"indexFilterSet" : false,
"parsedQuery" : {a
"$and" : [
{
"incidentOn" : {
"$lt" : ISODate("2018-01-19T06:13:39.000Z")
}
},
{
"incidentOn" : {
"$gte" : ISODate("2017-12-19T00:00:00.000Z")
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 10,
"inputStage" : {
"stage" : "SKIP",
"skipAmount" : 7519340,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"incidentOn" : -1.0
},
"indexName" : "incidentOn_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"incidentOn" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"incidentOn" : [
"(new Date(1516342419000), new Date(1513641600000)]"
]
}
}
}
}
},
"rejectedPlans" : []
},
{
"shardName" : "rs1",
"connectionString" : "rs1/172.18.64.54:27017",
"serverInfo" : {
"host" : "UemCent7x64-76",
"port" : 27017,
"version" : "3.4.10",
"gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
},
"plannerVersion" : 1,
"namespace" : "mydatabase.rptDlp",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"incidentOn" : {
"$lt" : ISODate("2018-01-19T06:13:39.000Z")
}
},
{
"incidentOn" : {
"$gte" : ISODate("2017-12-19T00:00:00.000Z")
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 10,
"inputStage" : {
"stage" : "SKIP",
"skipAmount" : 7519340,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "SHARDING_FILTER",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"incidentOn" : -1.0
},
"indexName" : "incidentOn_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"incidentOn" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"incidentOn" : [
"(new Date(1516342419000), new Date(1513641600000)]"
]
}
}
}
}
}
},
"rejectedPlans" : []
}
]
}
},
"ok" : 1.0
}
解释查询结果(针对mongo分片):
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "mydatabase.rptDlp",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"incidentOn" : {
"$lt" : ISODate("2018-01-19T06:13:39.000Z")
}
},
{
"incidentOn" : {
"$gte" : ISODate("2017-12-19T00:00:00.000Z")
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 10,
"inputStage" : {
"stage" : "SKIP",
"skipAmount" : 7519340,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"incidentOn" : -1.0
},
"indexName" : "incidentOn_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"incidentOn" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"incidentOn" : [
"(new Date(1516342419000), new Date(1513641600000)]"
]
}
}
}
}
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "UemCent7x64-69",
"port" : 27017,
"version" : "3.4.10",
"gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
},
"ok" : 1.0
}
任何建议都会有所帮助,在此先感谢。
结果:
当我们对mongos(路由器)和shard执行skip()查询时,它的行为方式不同。
在分片上执行skip(n)和limit(m)时,它实际上会跳过' n'记录数量,仅返回' m'限制中提到的记录。 但是这不可能通过mongos,因为数据可能在多个分片上被分割,并且由于哪个分片可能包含少于' n'记录数量(在跳过中提到)。 因此,mongos不是应用skip(n)查询,而是通过添加跳过计数n和限制计数m来收集所有记录,从而对分片执行limit(n + m)查询。收集所有碎片mongos的结果后,将跳过已组装的记录。 此外,如果数据很大,mongos会使用getMore命令以块的形式提取数据,这也会降低性能。
根据mongo doc引用:https://docs.mongodb.com/v3.0/core/sharded-cluster-query-router/ 如果查询使用limit()游标方法限制结果集的大小,则mongos实例将该限制传递给分片,然后在将结果返回给客户端之前将限制重新应用于结果。
如果查询使用skip()游标方法指定要跳过的记录数,则mongos无法将跳过传递给分片,而是从分片中检索未提取的结果,并在组装完成时跳过适当数量的文档结果。但是,当与limit()一起使用时,mongos会将限制加上skip()的值传递给分片,以提高这些操作的效率。
是否有任何解决方案可以改善通过mongos(路由器)执行的跳过查询性能?
提前致谢。
答案 0 :(得分:0)
如果您有一个HASHed分片键,您将无法使用范围查询来查找每个项目所在的节点,因此需要扫描分片群集中的所有节点。因此,最慢的查询将是集合中最慢的节点加上时间来汇总mongos上的结果,然后再将它们发送回客户端。
使用HASHed分片键将结果分散到整个群集中,这样您就只能根据密钥匹配进行查询。
查看此处的文档 - https://docs.mongodb.com/manual/core/hashed-sharding/
如果您不介意查询执行完整的群集扫描,那么您可以通过在incidentOn
上添加标准索引来提高效率,这将使查询在每个节点上更快但仍然赢得& #39;能够精确定位集群中的节点。