mongodb性能調(diào)優(yōu)初探

> show dbs
admin  0.000GB
local  0.000GB
yang   0.615GB
> use yang
switched to db yang
> show collections
system.profile
users
> db.users.find().count()
10000000

這里選取一千萬(wàn)條數(shù)據(jù)來(lái)說(shuō)明問(wèn)題,這些數(shù)據(jù)都是沒(méi)有索引的,首先我們要做的是找出慢查詢(xún)。
1.開(kāi)啟慢查詢(xún)分析器
db.setProfilingLevel(1,300)
第一個(gè)參數(shù)“1”表示記錄慢查詢(xún),還可以選擇“0”或者“2”,分別代表不記錄數(shù)據(jù)和記錄所有讀寫(xiě)操作,我們一般會(huì)設(shè)置成“1”,第二個(gè)參數(shù)代表慢查詢(xún)閾值,只有查詢(xún)執(zhí)行時(shí)間超過(guò)300ms才會(huì)記錄。
2.查詢(xún)所記錄的慢查詢(xún)
監(jiān)控結(jié)果保存在一個(gè)特殊的蓋子集合system.profile里,這個(gè)集合分配了128kb的空間,要確保監(jiān)控分析數(shù)據(jù)不會(huì)消耗太多的系統(tǒng)性資源;蓋子集合維護(hù)了自然的插入順序,可以使用$natural操作符進(jìn)行排序,
> db.system.profile.find().sort({'$natural':-1}).limit(5).pretty()會(huì)打印出最耗時(shí)的5條查詢(xún),在這里我們選取其中的一條來(lái)看:

{
        "op" : "command",
        "ns" : "yang.users",
        "command" : {
                "explain" : {
                        "find" : "users",        //這里是所查詢(xún)的集合
                       //重點(diǎn)在這里,查詢(xún)條件是 "username" = "dede"
                        "filter" : {
                                "username" : "dede"
                        }
                },
                "verbosity" : "allPlansExecution"
        },
        "numYield" : 78375,
        "locks" : {
                "Global" : {
                        "acquireCount" : {
                                "r" : NumberLong(156752)
                        }
                },
                "Database" : {
                        "acquireCount" : {
                                "r" : NumberLong(78376)
                        }
                },
                "Collection" : {
                        "acquireCount" : {
                                "r" : NumberLong(78376)
                        }
                }
        },
        "responseLength" : 848,
        "protocol" : "op_command",
        "millis" : 6800,
        "ts" : ISODate("2018-02-05T10:35:07.576Z"),
        "client" : "127.0.0.1",
        "appName" : "MongoDB Shell",
        "allUsers" : [ ],
        "user" : ""
}

3.上面的還不夠直觀(guān),我們現(xiàn)在構(gòu)造這個(gè)查詢(xún)并用explain來(lái)分析具體慢在了那里:
> db.users.find({"username":"dede"}).explain('executionStats')
explain的入?yún)⒖蛇x值為:
?"queryPlanner" 是默認(rèn)值,表示僅僅展示執(zhí)行計(jì)劃信息;
?"executionStats" 表示展示執(zhí)行計(jì)劃信息同時(shí)展示被選中的執(zhí)行計(jì)劃的執(zhí)行情況信息;
? "allPlansExecution" 表示展示執(zhí)行計(jì)劃信息,并展示被選中的執(zhí)行計(jì)劃的執(zhí)行情況信息,還展示備選的執(zhí)行計(jì)劃的執(zhí)行情況信息;
我們一般會(huì)選用executionStats,打印出來(lái)的內(nèi)容如下:

> db.users.find({"username":"dede"}).explain('executionStats')
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "yang.users",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "username" : {
                                "$eq" : "dede"
                        }
                },
         //執(zhí)行計(jì)劃會(huì)有多種,這里列出的是勝出的執(zhí)行計(jì)劃
                "winningPlan" : {
         //重點(diǎn)看下面的stage,collscan代表全表掃描,如果命中索引這里應(yīng)該是ixscan,index scan
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "username" : {
                                        "$eq" : "dede"
                                }
                        },
                        "direction" : "forward"
                },
         //下面列舉的是沒(méi)有使用的執(zhí)行計(jì)劃
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 1428802,
                "executionTimeMillis" : 7080,
                "totalKeysExamined" : 0,
                "totalDocsExamined" : 10000000,
                "executionStages" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "username" : {
                                        "$eq" : "dede"
                                }
                        },
                        "nReturned" : 1428802,//查詢(xún)所返回的條數(shù)
                        "executionTimeMillisEstimate" : 6633,//執(zhí)行總時(shí)間
                        "works" : 10000002,
                        "advanced" : 1428802,
                        "needTime" : 8571199,
                        "needYield" : 0,
                        "saveState" : 78368,
                        "restoreState" : 78368,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "direction" : "forward",
                        "docsExamined" : 10000000//總共掃描了一千萬(wàn)條數(shù)據(jù)
                }
        },
        "serverInfo" : {
                "host" : "localhost.localdomain",
                "port" : 27022,
                "version" : "3.4.10",
                "gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
        },
        "ok" : 1  
}

講上面的結(jié)果簡(jiǎn)化如下:

queryPlanner(執(zhí)行計(jì)劃描述)
        winningPlan(被選中的執(zhí)行計(jì)劃) 
                stage(可選項(xiàng):COLLSCAN 沒(méi)有走索引;IXSCAN使用了索引) 
        rejectedPlans(候選的執(zhí)行計(jì)劃) 
 executionStats(執(zhí)行情況描述)
        nReturned (返回的文檔個(gè)數(shù)) 
        executionTimeMillis(執(zhí)行時(shí)間ms) 
        totalKeysExamined (檢查的索引鍵值個(gè)數(shù)) 
        totalDocsExamined (檢查的文檔個(gè)數(shù))

我們最終的優(yōu)化目標(biāo)

1.根據(jù)需求建立索引
2.每個(gè)查詢(xún)都要使用索引以提高查詢(xún)效率, winningPlan. stage 必須為IXSCAN ;
3.追求totalDocsExamined = nReturned
屬性名 類(lèi)型 說(shuō)明
background boolean 是否后臺(tái)構(gòu)建索引,在生產(chǎn)環(huán)境中,如果數(shù)據(jù)量太大,構(gòu)建索引可能會(huì)消耗很長(zhǎng)時(shí)間,為了不影響業(yè)務(wù),可以加上此參數(shù),后臺(tái)運(yùn)行同時(shí)還會(huì)為其他讀寫(xiě)操作讓路
unique boolean 是否為唯一索引
name string 索引名字
sparse boolean 是否為稀疏索引,索引僅引用具有指定字段的文檔。

那么mongodb的索引又是怎么建立的呢?
首先,mongodb的索引分為單建索引db.users. createIndex({age:-1}); 、復(fù)合索引db.users. createIndex({username:1,age:-1,country:1});、多鍵索引(在數(shù)組的屬性上建立索引)db.users. createIndex({favorites.city:1}),
創(chuàng)建索引的語(yǔ)法如下:
db.collection.createIndex(keys, options)
?語(yǔ)法中 Key 值為要?jiǎng)?chuàng)建的索引字段,1為指定按升序創(chuàng)建索引,如果你想按降序來(lái)創(chuàng)建索引指定為-1,也可以指定為hashed(哈希索引)。
?語(yǔ)法中options為索引的屬性,屬性說(shuō)明見(jiàn)下表;

屬性名 類(lèi)型 說(shuō)明
background boolean 是否后臺(tái)構(gòu)建索引,在生產(chǎn)環(huán)境中,如果數(shù)據(jù)量太大,構(gòu)建索引可能會(huì)消耗很長(zhǎng)時(shí)間,為了不影響業(yè)務(wù),可以加上此參數(shù),后臺(tái)運(yùn)行同時(shí)還會(huì)為其他讀寫(xiě)操作讓路
unique boolean 是否為唯一索引
name string 索引名字
sparse boolean 是否為稀疏索引,索引僅引用具有指定字段的文檔。

舉例如下:
?單鍵唯一索引:db.users. createIndex({username :1},{unique:true});
?單鍵唯一稀疏索引:db.users. createIndex({username :1},{unique:true,sparse:true});
?復(fù)合唯一稀疏索引:db.users. createIndex({username:1,age:-1},{unique:true,sparse:true});
?創(chuàng)建哈希索引并后臺(tái)運(yùn)行:db.users. createIndex({username :'hashed'},{background:true});
?刪除索引
?根據(jù)索引名字刪除某一個(gè)指定索引:db.users.dropIndex("username_1");
?刪除某集合上所有索引:db.users.dropIndexs();
?重建某集合上所有索引:db.users.reIndex();
?查詢(xún)集合上所有索引:db.users.getIndexes();
既然已經(jīng)知道了如何創(chuàng)建索引,那么我們就給users表的username字段創(chuàng)建索引
> db.users.createIndex({"username":1},{"name":"username_1","sparse":true,"background":true});
看看有沒(méi)有創(chuàng)建成功

> db.users.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "yang.users"
        },
        {
                "v" : 2,
                "key" : {
                        "username" : 1
                },
                "name" : "username_1",
                "ns" : "yang.users",
                "sparse" : true,
                "background" : true
        }
]

可以看到索引已經(jīng)創(chuàng)建成功,索引得名字就是username_1,我們?cè)俨榭匆幌聢?zhí)行計(jì)劃

> db.users.find({"username":"dede"}).explain("executionStats")
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "yang.users",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "username" : {
                                "$eq" : "dede"
                        }
                },
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "username" : 1
                                },
                                "indexName" : "username_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "username" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : true,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "username" : [
                                                "[\"dede\", \"dede\"]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 1428802,
                "executionTimeMillis" : 4399,
                "totalKeysExamined" : 1428802,
                "totalDocsExamined" : 1428802,
                "executionStages" : {
                        "stage" : "FETCH",
                        "nReturned" : 1428802,
                        "executionTimeMillisEstimate" : 4002,
                        "works" : 1428803,
                        "advanced" : 1428802,
                        "needTime" : 0,
                        "needYield" : 0,
                        "saveState" : 11308,
                        "restoreState" : 11308,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "docsExamined" : 1428802,
                        "alreadyHasObj" : 0,
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 1428802,
                                "executionTimeMillisEstimate" : 765,
                                "works" : 1428803,
                                "advanced" : 1428802,
                                "needTime" : 0,
                                "needYield" : 0,
                                "saveState" : 11308,
                                "restoreState" : 11308,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "keyPattern" : {
                                        "username" : 1
                                },
                                "indexName" : "username_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "username" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : true,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "username" : [
                                                "[\"dede\", \"dede\"]"
                                        ]
                                },
                                "keysExamined" : 1428802,
                                "seeks" : 1,
                                "dupsTested" : 0,
                                "dupsDropped" : 0,
                                "seenInvalidated" : 0
                        }
                }
        },
        "serverInfo" : {
                "host" : "localhost.localdomain",
                "port" : 27022,
                "version" : "3.4.10",
                "gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
        },
        "ok" : 1
}

我們可以看到,查詢(xún)時(shí)間從原來(lái)的6s變成了765ms,并且執(zhí)行計(jì)劃中的stage:IXSCAN,再看看"docsExamined" : 1428802,"nReturned" : 1428802,,兩者相等,至此我們完成了mongodb的初步調(diào)優(yōu),這里只是列舉的非常簡(jiǎn)單的場(chǎng)景,具體的調(diào)優(yōu)還是要看各位的實(shí)際情況去分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 學(xué)習(xí)《MongoDB 權(quán)威指南·第2版》的筆記,結(jié)合 MongoDB 官方最新文檔(v3.6),簡(jiǎn)單記錄一些概念、...
    小魚(yú)愛(ài)小蝦閱讀 6,206評(píng)論 0 5
  • 目錄 查詢(xún)操作 集合查詢(xún)方法 find() 查詢(xún)內(nèi)嵌文檔 查詢(xún)操作符(內(nèi)含 數(shù)組查詢(xún)) "$gt" 、"$gte"...
    彩虹之夢(mèng)閱讀 1,118評(píng)論 0 1
  • 睡醒找何必帶我過(guò)了突變,看完了ig的比賽后安利大哥來(lái)打合作模式,把沃拉尊升到5級(jí)了。菜如我們也是困難隨便擼了!晚飯...
    AJI米閱讀 134評(píng)論 0 0
  • 歷經(jīng)人生滄桑時(shí),或許有種失落感,或許感到形單影只,這時(shí),總會(huì)有一種朋友,無(wú)須形影相隨,無(wú)須感天動(dòng)地,無(wú)須多言,便心...
    蔣書(shū)丹閱讀 221評(píng)論 0 0

友情鏈接更多精彩內(nèi)容