mongodb千万级数据分页优化及分析讲解
千万级数据分页优化
mongo采用单机部署,数据量千万级。要求获取分页,按照capTime闪回排列,每个数据页20条
- skip+limit 这是最传统的数据查询方法,
db.getCollection('CapMotor').find() .skip( 9000000).sort({'capTime':1}).limit (20);
skip 后跟 pageSize*pageIndex,limit 后跟 pageSize。这种方法在数据量达到百万级的时候还是有用的,但是过了千万级之后就不再可用了。我这里跟踪的题时间大概是9s左右,这显然是不可能的,而且影响效率。推荐使用第二种方法 - 前端传入临界值id,查询并添加条件,从当前位置截取,取20条
前端请求下一页数据时说一下当前页面的最后一条数据,capTime栏被跳过,值:1548482420,同时也传递了该数据对应的objectid,对应的值:ObjectId("5bc5ce033b071d2fa84e60f4"),如果相同的话有时,在某些匹配的数据块中必须传递。查询mongo时,添加capTime大于等于1548482420的条件,并且不包含数据chunk。下面是对应的代码
// 数据查询下一页时:
db.getCollection('CapMotor').find({
"$and":[
{'capTime' :{ "$gte" :1548482420}},
{'_id':{"$ne":ObjectId("5bc5ce033b071d2fa84e60f4")}}
]
})
.sort({'capTime':1}).limit(20)
复制代码
注:以上是我根据具体领域的想法。排序,这个字段的值可以有很多。事实上,在发展过程中,这个领域的价值可能是独一无二的。对应的代码如下
// 数据查询下一页时:
db.getCollection('CapMotor').find({'capTime' :{ "$gt" :1539582402} }).sort({'capTime':1}).limit(20)
复制代码
第一种方法,前端可以确定要转到首页。最后一页、前后一页、中间指定页,但仅限数据量小于100万时;第二种方法速度较快,但前端无法跳转到指定页码、首页、末页、上一页、下一页。这个有可能;
mongodb分析器讲解
本次以3.4.15-49-g4ef027f为例。每个版本的执行计划都不同
/* 1 */
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "bigdata.faceCapture",
"indexFilterSet" : false,
"parsedQuery" : {
"fcap_id" : {
"$lt" : "fd129550-ced3-11e8-8ea8-1866daf63d9f"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"fcap_id" : 1
},
"indexName" : "fcap_id_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"fcap_id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"fcap_id" : [
"[\"\", \"fd129550-ced3-11e8-8ea8-1866daf63d9f\")"
]
}
}
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "master",
"port" : 27017,
"version" : "3.4.15-49-g4ef027f",
"gitVersion" : "4ef027f98d5c00a0f4e507cbe39a22cab4c7a44c"
},
"ok" : 1.0
}
复制代码
只要关注queryPlanner中的winingPlan,定义.queryPlanner.winingPlan.stage:最优执行计划阶段。这里的返回是FETCH,可以理解为通过返回索引位置来检索特定的文档(该阶段有几种模式,后面会详细解释)。
Explain.queryPlanner.writingPlan.inputStage:用于解释子阶段,并为父阶段提供文档和关键字索引。
explain.queryPlanner.writingPlan.stage的子阶段,这是IXSCAN,表示正在执行索引扫描。每个状态值
的解释如下: COLLSCAN:全表扫描
IXSCAN:索引扫描
FETCH::从每个 shard_SHARD 中获取索引指定的文档数据_SHARD:用于合并
SORT :表示已经在内存中排序(对应之前版本中的 scanAndOrder:true)
SORT_MERGE:表示已经在内存中排序,然后合并
使用限制:LIMIT 返回次数
SKIP:使用skip
IDHACK:查询_id
SHARDING_FILTER:通过mongos 查询分片数据
ll。像db一样使用它。 COUNTSCAN:计数不使用索引计数时返回阶段
COUNT_SCAN:使用索引计数时返回阶段
SUBPLA:不使用索引时返回$或查询阶段
TEXT:全文索引询问时,阶段产生
。 faceCapture 集合中有超过 1000 万条数据。有复合索引fcap_time、fcap_dcid、person_id
{
"fcap_time" : -1,
"fcap_dcid" : 1,
"person_id" : 1
}
复制代码
单行索引fcap_time、fcap_id
{
"fcap_time" : 1
}
复制代码
{
"fcap_id" : 1
}
复制代码
使用关键字explain进行SQL分析,结果如下查询:
// 走的是复合索引 注意索引的顺序,fcap_time" : -1, "fcap_dcid" : 1, "person_id" : 1
db.getCollection('faceCapture').find({'fcap_time' :{ "$gt" :1539745381}}).explain()
// 以下两个字段都是复合索引中,未走索引,
db.getCollection('faceCapture').find({'fcap_dcid' :{ "$lt" :"fd129550-ced3-11e8-8ea8-1866daf63d9f"}}).explain()
db.getCollection('faceCapture').find({'person_id' :{ "$lt" :"fd129550-ced3-11e8-8ea8-1866daf63d9f"}}).explain()
// 走的是索引 fcap_id" : -1,
db.getCollection('faceCapture').find({'fcap_id' :{ "$lt" :"fd129550-ced3-11e8-8ea8-1866daf63d9f"}}).explain()
db.getCollection('faceCapture').find({'fcap_id' :{ "$lt" :"fd129550-ced3-11e8-8ea8-1866daf63d9f"}}).count()
复制代码
以下结论♶ Mongodb索引也有原理最多为前缀,与mysql中的关键字相同;复合索引和单列索引有相同的列,并且该列位于复合索引的第一位,到达位置时默认使用复合索引;当查询复合索引的非首位置时,默认不使用该索引;
作者:LXH
来源:掘金
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。