MongoDB:查询性能下降
MongoDB: degraded query performance
我在 MongoDB 中有一个用户集合,其中包含超过 250 万条记录,共计 30 GB。我有大约 4 到 6 GB 的索引。它处于具有两个分片的分片环境中,每个分片由副本集组成。服务器专门用于 Mongo,没有开销。总 RAM 超过 10 GB,足以满足我正在执行的查询类型(如下所示)。
我担心的是,尽管有适当字段的索引,但检索结果的时间很长(2 分钟到高达 30 分钟),这是不可接受的。我是 MongoDB 的新手,对于为什么会这样,我真的很困惑。
示例架构是:
user:
{
_id: UUID (indexed by default),
name: string,
dob: ISODate,
addr: string,
createdAt: ISODate (indexed),
.
.
.,
transaction:[
{
firstTransaction: ISODate(indexed),
lastTransaction: ISODate(indexed),
amount: float,
product: string (indexed),
.
.
.
},...
],
other sub documents...
}
子文档长度在 0-50 之间变化。
我执行的查询是:
1) db.user.find().min({createdAt:ISODate("2014-12-01")}).max({createdAt:ISODate("2014-12-31")}).explain()
这个查询起初运行缓慢,但后来快如闪电(我猜是因为预热)。
2) db.user.find({transaction:{$elemMatch:{product:'mobile'}}}).explain()
此查询花费了 30 多分钟,预热没有帮助,因为每次性能都相同。它返回了超过一半的集合。
3) db.user.find({transaction:{$elemMatch:{product:'mobile'}}, firstTransaction:{$in:[ISODate("2015-01-01"),ISODate("2015-01-02")]}}}}).explain()
这是我想要提高性能的主要查询。但不幸的是,这个查询需要 30 多分钟才能执行。我尝试了很多版本,例如:
db.user.find({transaction:{$elemMatch:{product:'mobile'}}}).min({transaction:{$elemMatch:{firstTransaction:ISODate("2015-01-01")}}}).max({transaction:{$elemMatch:{firstTransaction:ISODate("2015-01-02")}}}).explain()
这个查询给我错误:
planner returned error: unable to find relevant index for max/min
query & with hint():
planner returned error: hint provided does not work with min query
我使用 min max 函数是因为 MongoDB 中使用 $lt、$gt 运算符的范围查询的不确定性,有时会忽略任何一个绑定并最终扫描比需要更多的文档。
我使用了如下索引:
db.user.ensureIndex({createdAt: 1})
db.user.ensureIndex({"transaction.firstTransaction":1})
db.user.ensureIndex({"transaction.lastTransaction":1})
db.user.ensureIndex({"transaction.product":1})
我尝试对 3 查询使用复合索引,即:
db.user.ensureIndex({"transaction.firstTransaction":1, "transaction.product":1})
但这似乎没有给我任何结果。查询卡住并且永远不会 returns 结果。我是认真的。绝不。就像死锁一样。我不知道为什么。所以我放弃了这个索引并在等待半个多小时后得到了结果(真的很沮丧)。
请帮助我,因为我真的很想找到解决方案和想法。
此输出可能有帮助:
Following is the output for:
查询:
db.user.find({transaction:{$elemMatch:{product:"mobile", firstTransaction:{$gte:ISODate("2015-01-01"), $lt:ISODate("2015-01-02")}}}}).hint("transaction.firstTransaction_1_transaction.product_1").explain()
输出:
{
"clusteredType" : "ParallelSort",
"shards" : {
"test0/mrs00.test.com:27017,mrs01.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1_transaction.firstTransaction_1",
"isMultiKey" : true,
"n" : 622,
"nscannedObjects" : 350931,
"nscanned" : 352000,
"nscannedObjectsAllPlans" : 350931,
"nscannedAllPlans" : 352000,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 119503,
"nChunkSkips" : 0,
"millis" : 375693,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
],
"transaction.firstTransaction" : [
[
true,
ISODate("2015-01-02T00:00:00Z")
]
]
},
"server" : "ip-12-0-0-31:27017",
"filterSet" : false
}
],
"test1/mrs10.test.com:27017,mrs11.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1_transaction.firstTransaction_1",
"isMultiKey" : true,
"n" : 547,
"nscannedObjects" : 350984,
"nscanned" : 352028,
"nscannedObjectsAllPlans" : 350984,
"nscannedAllPlans" : 352028,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 132669,
"nChunkSkips" : 0,
"millis" : 891898,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
],
"transaction.firstTransaction" : [
[
true,
ISODate("2015-01-02T00:00:00Z")
]
]
},
"server" : "ip-12-0-0-34:27017",
"filterSet" : false
}
]
},
"cursor" : "BtreeCursor transaction.product_1_transaction.firstTransaction_1",
"n" : 1169,
"nChunkSkips" : 0,
"nYields" : 252172,
"nscanned" : 704028,
"nscannedAllPlans" : 704028,
"nscannedObjects" : 701915,
"nscannedObjectsAllPlans" : 701915,
"millisShardTotal" : 1267591,
"millisShardAvg" : 633795,
"numQueries" : 2,
"numShards" : 2,
"millis" : 891910
}
查询:
db.user.find({transaction:{$elemMatch:{product:'mobile'}}}).explain()
输出:
{
"clusteredType" : "ParallelSort",
"shards" : {
"test0/mrs00.test.com:27017,mrs01.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1",
"isMultiKey" : true,
"n" : 553072,
"nscannedObjects" : 553072,
"nscanned" : 553072,
"nscannedObjectsAllPlans" : 553072,
"nscannedAllPlans" : 553072,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 164888,
"nChunkSkips" : 0,
"millis" : 337909,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
]
},
"server" : "ip-12-0-0-31:27017",
"filterSet" : false
}
],
"test1/mrs10.test.com:27017,mrs11.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1",
"isMultiKey" : true,
"n" : 554176,
"nscannedObjects" : 554176,
"nscanned" : 554176,
"nscannedObjectsAllPlans" : 554176,
"nscannedAllPlans" : 554176,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 107496,
"nChunkSkips" : 0,
"millis" : 327928,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
]
},
"server" : "ip-12-0-0-34:27017",
"filterSet" : false
}
]
},
"cursor" : "BtreeCursor transaction.product_1",
"n" : 1107248,
"nChunkSkips" : 0,
"nYields" : 272384,
"nscanned" : 1107248,
"nscannedAllPlans" : 1107248,
"nscannedObjects" : 1107248,
"nscannedObjectsAllPlans" : 1107248,
"millisShardTotal" : 665837,
"millisShardAvg" : 332918,
"numQueries" : 2,
"numShards" : 2,
"millis" : 337952
}
如果我遗漏了任何细节,请告诉我。
谢谢。
1st:您的查询过于复杂。过于频繁地使用 $elemMatch。
第二:如果你可以在查询中包含你的分片键,它将大大提高速度。
我将为您优化查询:
db.user.find({
createdAt: {
$gte: ISODate("2014-12-01"),
$lte: ISODate("2014-12-31")
}
}).explain()
db.user.find({
'transaction.product':'mobile'
}).explain()
db.user.find({
'transaction.product':'mobile',
firstTransaction:{
$in:[
ISODate("2015-01-01"),
ISODate("2015-01-02")
]
}
}).explain()
底线是:每次都包含您的分片键可以节省时间。
它甚至可以节省循环遍历分片键并多次进行相同查询的时间。
性能下降的原因是大型工作集。对于某些查询(主要是范围查询),设置超出了物理限制并发生了页面错误。由于这种性能下降。
我所做的一种解决方案是为查询应用一些过滤器,这将限制结果集并尝试执行相等性检查而不是范围(遍历范围)。
这些调整对我有用。希望对其他人也有帮助。
我在 MongoDB 中有一个用户集合,其中包含超过 250 万条记录,共计 30 GB。我有大约 4 到 6 GB 的索引。它处于具有两个分片的分片环境中,每个分片由副本集组成。服务器专门用于 Mongo,没有开销。总 RAM 超过 10 GB,足以满足我正在执行的查询类型(如下所示)。
我担心的是,尽管有适当字段的索引,但检索结果的时间很长(2 分钟到高达 30 分钟),这是不可接受的。我是 MongoDB 的新手,对于为什么会这样,我真的很困惑。
示例架构是:
user:
{
_id: UUID (indexed by default),
name: string,
dob: ISODate,
addr: string,
createdAt: ISODate (indexed),
.
.
.,
transaction:[
{
firstTransaction: ISODate(indexed),
lastTransaction: ISODate(indexed),
amount: float,
product: string (indexed),
.
.
.
},...
],
other sub documents...
}
子文档长度在 0-50 之间变化。
我执行的查询是:
1) db.user.find().min({createdAt:ISODate("2014-12-01")}).max({createdAt:ISODate("2014-12-31")}).explain()
这个查询起初运行缓慢,但后来快如闪电(我猜是因为预热)。
2) db.user.find({transaction:{$elemMatch:{product:'mobile'}}}).explain()
此查询花费了 30 多分钟,预热没有帮助,因为每次性能都相同。它返回了超过一半的集合。
3) db.user.find({transaction:{$elemMatch:{product:'mobile'}}, firstTransaction:{$in:[ISODate("2015-01-01"),ISODate("2015-01-02")]}}}}).explain()
这是我想要提高性能的主要查询。但不幸的是,这个查询需要 30 多分钟才能执行。我尝试了很多版本,例如:
db.user.find({transaction:{$elemMatch:{product:'mobile'}}}).min({transaction:{$elemMatch:{firstTransaction:ISODate("2015-01-01")}}}).max({transaction:{$elemMatch:{firstTransaction:ISODate("2015-01-02")}}}).explain()
这个查询给我错误:
planner returned error: unable to find relevant index for max/min query & with hint():
planner returned error: hint provided does not work with min query
我使用 min max 函数是因为 MongoDB 中使用 $lt、$gt 运算符的范围查询的不确定性,有时会忽略任何一个绑定并最终扫描比需要更多的文档。
我使用了如下索引:
db.user.ensureIndex({createdAt: 1})
db.user.ensureIndex({"transaction.firstTransaction":1})
db.user.ensureIndex({"transaction.lastTransaction":1})
db.user.ensureIndex({"transaction.product":1})
我尝试对 3 查询使用复合索引,即:
db.user.ensureIndex({"transaction.firstTransaction":1, "transaction.product":1})
但这似乎没有给我任何结果。查询卡住并且永远不会 returns 结果。我是认真的。绝不。就像死锁一样。我不知道为什么。所以我放弃了这个索引并在等待半个多小时后得到了结果(真的很沮丧)。
请帮助我,因为我真的很想找到解决方案和想法。
此输出可能有帮助:
Following is the output for:
查询:
db.user.find({transaction:{$elemMatch:{product:"mobile", firstTransaction:{$gte:ISODate("2015-01-01"), $lt:ISODate("2015-01-02")}}}}).hint("transaction.firstTransaction_1_transaction.product_1").explain()
输出:
{
"clusteredType" : "ParallelSort",
"shards" : {
"test0/mrs00.test.com:27017,mrs01.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1_transaction.firstTransaction_1",
"isMultiKey" : true,
"n" : 622,
"nscannedObjects" : 350931,
"nscanned" : 352000,
"nscannedObjectsAllPlans" : 350931,
"nscannedAllPlans" : 352000,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 119503,
"nChunkSkips" : 0,
"millis" : 375693,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
],
"transaction.firstTransaction" : [
[
true,
ISODate("2015-01-02T00:00:00Z")
]
]
},
"server" : "ip-12-0-0-31:27017",
"filterSet" : false
}
],
"test1/mrs10.test.com:27017,mrs11.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1_transaction.firstTransaction_1",
"isMultiKey" : true,
"n" : 547,
"nscannedObjects" : 350984,
"nscanned" : 352028,
"nscannedObjectsAllPlans" : 350984,
"nscannedAllPlans" : 352028,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 132669,
"nChunkSkips" : 0,
"millis" : 891898,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
],
"transaction.firstTransaction" : [
[
true,
ISODate("2015-01-02T00:00:00Z")
]
]
},
"server" : "ip-12-0-0-34:27017",
"filterSet" : false
}
]
},
"cursor" : "BtreeCursor transaction.product_1_transaction.firstTransaction_1",
"n" : 1169,
"nChunkSkips" : 0,
"nYields" : 252172,
"nscanned" : 704028,
"nscannedAllPlans" : 704028,
"nscannedObjects" : 701915,
"nscannedObjectsAllPlans" : 701915,
"millisShardTotal" : 1267591,
"millisShardAvg" : 633795,
"numQueries" : 2,
"numShards" : 2,
"millis" : 891910
}
查询:
db.user.find({transaction:{$elemMatch:{product:'mobile'}}}).explain()
输出:
{
"clusteredType" : "ParallelSort",
"shards" : {
"test0/mrs00.test.com:27017,mrs01.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1",
"isMultiKey" : true,
"n" : 553072,
"nscannedObjects" : 553072,
"nscanned" : 553072,
"nscannedObjectsAllPlans" : 553072,
"nscannedAllPlans" : 553072,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 164888,
"nChunkSkips" : 0,
"millis" : 337909,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
]
},
"server" : "ip-12-0-0-31:27017",
"filterSet" : false
}
],
"test1/mrs10.test.com:27017,mrs11.test.com:27017" : [
{
"cursor" : "BtreeCursor transaction.product_1",
"isMultiKey" : true,
"n" : 554176,
"nscannedObjects" : 554176,
"nscanned" : 554176,
"nscannedObjectsAllPlans" : 554176,
"nscannedAllPlans" : 554176,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 107496,
"nChunkSkips" : 0,
"millis" : 327928,
"indexBounds" : {
"transaction.product" : [
[
"mobile",
"mobile"
]
]
},
"server" : "ip-12-0-0-34:27017",
"filterSet" : false
}
]
},
"cursor" : "BtreeCursor transaction.product_1",
"n" : 1107248,
"nChunkSkips" : 0,
"nYields" : 272384,
"nscanned" : 1107248,
"nscannedAllPlans" : 1107248,
"nscannedObjects" : 1107248,
"nscannedObjectsAllPlans" : 1107248,
"millisShardTotal" : 665837,
"millisShardAvg" : 332918,
"numQueries" : 2,
"numShards" : 2,
"millis" : 337952
}
如果我遗漏了任何细节,请告诉我。
谢谢。
1st:您的查询过于复杂。过于频繁地使用 $elemMatch。 第二:如果你可以在查询中包含你的分片键,它将大大提高速度。
我将为您优化查询:
db.user.find({
createdAt: {
$gte: ISODate("2014-12-01"),
$lte: ISODate("2014-12-31")
}
}).explain()
db.user.find({
'transaction.product':'mobile'
}).explain()
db.user.find({
'transaction.product':'mobile',
firstTransaction:{
$in:[
ISODate("2015-01-01"),
ISODate("2015-01-02")
]
}
}).explain()
底线是:每次都包含您的分片键可以节省时间。
它甚至可以节省循环遍历分片键并多次进行相同查询的时间。
性能下降的原因是大型工作集。对于某些查询(主要是范围查询),设置超出了物理限制并发生了页面错误。由于这种性能下降。 我所做的一种解决方案是为查询应用一些过滤器,这将限制结果集并尝试执行相等性检查而不是范围(遍历范围)。 这些调整对我有用。希望对其他人也有帮助。