1. 程式人生 > >mongo千萬級資料優化

mongo千萬級資料優化

千萬級資料分頁優化

mongo採用的是單機部署,資料量1千萬,需求是實現分頁面,按照capTime倒敘排列,每頁資料20條

  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,並且不包含這幾條資料,以下是對應的程式碼

//  資料查詢下一頁時:
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)
複製程式碼

第一種方法,前端可以指定調到首頁,尾頁,上下一頁,中間指定頁,但是前提是資料量不過百萬的情況下; 第二種方法執行比較快,但是前端不能跳到指定的頁數,首頁,尾頁,上下一頁這種是可以的;

mongodb 分析器explain

本次是以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裡的winningPlan即可, explain.queryPlanner.winningPlan.stage:最優執行計劃的stage,這裡返回是FETCH,可以理解為通過返回的index位置去檢索具體的文件(stage有數個模式,將在後文中進行詳解)。

Explain.queryPlanner.winningPlan.inputStage:用來描述子stage,並且為其父stage提供文件和索引關鍵字。

explain.queryPlanner.winningPlan.stage的child stage,此處是IXSCAN,表示進行的是index scanning。

state各個值的解釋如下: COLLSCAN :全表掃描

IXSCAN:索引掃描

FETCH::根據索引去檢索指定document

SHARD_MERGE:各個分片返回資料進行merge

SORT:表明在記憶體中進行了排序(與前期版本的scanAndOrder:true一致)

SORT_MERGE:表明在記憶體中進行了排序後再合併

LIMIT:使用limit限制返回數

SKIP:使用skip進行跳過

IDHACK:針對_id進行查詢

SHARDING_FILTER:通過mongos對分片資料進行查詢

COUNT:利用db.coll.count()之類進行count運算

COUNTSCAN:count不使用用Index進行count時的stage返回

COUNT_SCAN:count使用了Index進行count時的stage返回

SUBPLA:未使用到索引的$or查詢的stage返回

TEXT:使用全文索引進行查詢時候的stage返回

集合faceCapture中有資料1千萬條以上的資料,有複合索引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中的like關鍵字; 複合索引和單列索引中都有同一列,並且該列是位於複合索引的第一個位置時,預設走的是複合索引; 複合索引的非首位查詢時,預設不走索引;