mongodb千万级数据分页优化与分析器explain

2018-10-1910:59:06数据库教程Comments6,275 views字数 3287阅读模式

千万级数据分页优化

mongo采用的是单机部署,数据量1千万,需求是实现分页面,按照capTime倒叙排列,每页数据20条文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

  1. skip+limit 这是最传统的数据查询方式,db.getCollection('CapMotor').find().skip(9000000).sort({'capTime':1}).limit(20);skip后面是pageSize*pageIndex,limit后是pageSize 这种方式在数据量是百万的时候,还凑合着用,但是千万以后,就不可用了,我这边跟踪的查询时间是9s左右,明显不行,影响效率,建议采用第2中方法
  2. 前端传临界值id,查询添加条件,从当前位置往后截取,取20条

前端在查询下一页数据的时候讲,当前页数据的最后一条,capTime这个字段传过来,值是:1548482420,并且把该条数据对应的objectid也传过来,对应值:ObjectId("5bc5ce033b071d2fa84e60f4"),如果是相同的时间,对应的多条数据,要都传递过来,mongo查询的时候,添加条件capTime大于等于1548482420,并且不包含这几条数据,以下是对应的代码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

//  数据查询下一页时:
db.getCollection('CapMotor').find({
    "$and":[
              {'capTime' :{ "$gte" :1548482420}},
              {'_id':{"$ne":ObjectId("5bc5ce033b071d2fa84e60f4")}}
          ]
    })
.sort({'capTime':1}).limit(20)

复制代码

注:以上是我考虑的按照某个字段排序,并且该字段的值有可能会有多个的情况,实际在开发中,该字段的值有可能是唯一的,对应的代码如下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

//  数据查询下一页时:
db.getCollection('CapMotor').find({'capTime' :{ "$gt" :1539582402} }).sort({'capTime':1}).limit(20)
复制代码

第一种方法,前端可以指定调到首页,尾页,上下一页,中间指定页,但是前提是数据量不过百万的情况下; 第二种方法执行比较快,但是前端不能跳到指定的页数,首页,尾页,上下一页这种是可以的;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

mongodb 分析器explain

本次是以3.4.15-49-g4ef027f为例,各个版本的执行计划差异较大文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

/* 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里的winningPlan即可, explain.queryPlanner.winningPlan.stage:最优执行计划的stage,这里返回是FETCH,可以理解为通过返回的index位置去检索具体的文档(stage有数个模式,将在后文中进行详解)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

Explain.queryPlanner.winningPlan.inputStage:用来描述子stage,并且为其父stage提供文档和索引关键字。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

explain.queryPlanner.winningPlan.stage的child stage,此处是IXSCAN,表示进行的是index scanning。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

state各个值的解释如下: COLLSCAN :全表扫描文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

IXSCAN:索引扫描文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

FETCH::根据索引去检索指定document文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

SHARD_MERGE:各个分片返回数据进行merge文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

SORT:表明在内存中进行了排序(与前期版本的scanAndOrder:true一致)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

SORT_MERGE:表明在内存中进行了排序后再合并文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

LIMIT:使用limit限制返回数文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

SKIP:使用skip进行跳过文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

IDHACK:针对_id进行查询文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

SHARDING_FILTER:通过mongos对分片数据进行查询文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

COUNT:利用db.coll.count()之类进行count运算文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

COUNTSCAN:count不使用用Index进行count时的stage返回文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

COUNT_SCAN:count使用了Index进行count时的stage返回文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

SUBPLA:未使用到索引的$or查询的stage返回文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

TEXT:使用全文索引进行查询时候的stage返回文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

集合faceCapture中有数据1千万条以上的数据,有复合索引fcap_time、fcap_dcid、person_id文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

{
    "fcap_time" : -1,
    "fcap_dcid" : 1,
    "person_id" : 1
}
复制代码

单行索引fcap_time、fcap_id文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

{
    "fcap_time" : 1
}
复制代码
{
    "fcap_id" : 1
}
复制代码

用关键字explain进行sql分析,发现如下问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

// 走的是复合索引 注意索引的顺序,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()

复制代码

以下有如下结论文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

mongodb的索引也有最前缀原则,类似于mysql中的like关键字; 复合索引和单列索引中都有同一列,并且该列是位于复合索引的第一个位置时,默认走的是复合索引; 复合索引的非首位查询时,默认不走索引;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

作者:LXH
来源:掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/sjk/6857.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/sjk/6857.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定