问题描述
index({ account_id: 1,is_private: 1,visible_in_list: 1,sent_at: -1,user_id: 1,status: 1,type: 1,'tracking.last_opened_at' => -1 },{name: 'email_page_index'})
selector:
{"account_id"=>BSON::ObjectId('id'),"is_private"=>false,"visible_in_list"=>{:$in=>[true,false]},"status"=>{:$in=>["ok","queued","processing","Failed"]},"sent_at"=>{"$lte"=>2021-03-22 15:29:18 UTC},"tracking.last_opened_at"=>{"$gt"=>1921-03-22 15:29:18 UTC},"user_id"=>BSON::ObjectId('id')}
options: {:sort=>{"tracking.last_opened_at"=>-1}}
获胜方案如下
"inputStage": {
"stage": "SORT_KEY_GENERATOR","inputStage": {
"stage": "FETCH","filter": {
"$and": [
{
"account_id": {
"$eq": {
"$oid": "objectid"
}
}
},{
"is_private": {
"$eq": false
}
},{
"sent_at": {
"$lte": "2021-03-22T14:06:10.000Z"
}
},{
"tracking.last_opened_at": {
"$gt": "1921-03-22T14:06:10.716Z"
}
},{
"status": {
"$in": [
"Failed","ok","queued"
]
}
},{
"visible_in_list": {
"$in": [
false,true
]
}
}
]
},"inputStage": {
"stage": "IXSCAN","keyPattern": {
"user_id": 1
},"indexName": "user_id_1","ismultikey": false,"multikeyPaths": {
"user_id": []
},.....
被拒绝的计划有如下复合索引和形式
"rejectedplans": [
{
"stage": "FETCH","inputStage": {
"stage": "SORT","sortPattern": {
"tracking.last_opened_at": -1
},"inputStage": {
"stage": "SORT_KEY_GENERATOR","inputStage": {
"stage": "IXSCAN","keyPattern": {
"account_id": 1,"is_private": 1,"visible_in_list": 1,"sent_at": -1,"user_id": 1,"status": 1,"type": 1,"tracking.last_opened_at": -1
},"indexName": "email_page_index","multikeyPaths": {
"account_id": [],"is_private": [],"visible_in_list": [],"sent_at": [],"user_id": [],"status": [],"type": [],"tracking.last_opened_at": []
},"isUnique": false,
问题是winnerPlan慢,mongoid选择复合索引不是更好吗?有办法强制吗? 另外,如何查看每个单独 STAGE 的执行时间?
解决方法
我发布了一些有助于解决性能问题并使用适当索引的信息。请注意,这可能不是解决方案(该问题尚待讨论)。
...另外,我如何查看每个单独 STAGE 的执行时间?
为此,使用 explain 和 executionStats
verbosity mode 生成查询计划。
问题是winnerPlan 很慢,如果不是这样就更好了 mongoid 选择复合索引?有办法强制吗?
发布的计划显示 "stage": "SORT_KEY_GENERATOR"
,这意味着 sort 操作正在内存中执行(即不使用索引进行排序)。这将是性能缓慢的原因之一(或主要原因)。那么,如何让查询和排序使用索引呢?
单个复合索引可用于具有过滤器+排序操作的查询。那将是一个有效的索引和查询。但是,它要求以某种方式定义复合索引 - 需要遵循一些规则。请参阅 Sort and Non-prefix Subset of an Index 上的此主题 - 与本文中的情况相同。我引用文档中的示例进行说明:
假设有一个复合索引:{ a: 1,b: 1,c: 1,d: 1 }
并且,所有字段都用于带过滤器+排序的查询。理想的查询是有一个过滤器+排序,如下所示:
db.test.find( { a: "val1",b: "val2",c: 1949 } ).sort( { d: 1 })
请注意,查询过滤器具有三个带有 equality 条件的字段(没有 $gt
、$lt
等)。然后查询的排序具有索引的最后一个字段 d
。这是将索引用于查询的过滤器和排序操作的理想情况。
在您的情况下,这不能从发布的查询中应用。因此,要寻求解决方案,您可能必须定义一个新索引,以便利用规则索引的排序和非前缀子集。
有可能吗?这取决于您的应用程序和用例。我有一个这样的想法,它可能会有所帮助。创建一个如下所示的复合索引,看看它是如何工作的:
account_id: 1,is_private: 1
visible_in_list: 1,status: 1,user_id: 1,'tracking.last_opened_at': -1
我认为在查询的过滤器中使用条件 "tracking.last_opened_at"=>{"$gt"=>1921-03-22 15:29:18 UTC},
可能对索引的使用没有帮助。
此外,还包括一些详细信息,例如 MongoDB 服务器的版本、集合的大小和一些平台详细信息。一般来说,查询性能取决于很多因素,包括索引、RAM 内存、数据的大小和类型以及对数据的操作类型。
ESR 规则: 当对具有多个过滤条件和排序的查询使用复合索引时,有时Equality Sort Range 规则对于优化查询很有用。请参阅以下具有此类场景的帖子:MongoDB - Index not being used when sorting and limiting on ranged query