第四章:mongodb 命令行操作进程控制性能优化
生活随笔
收集整理的這篇文章主要介紹了
第四章:mongodb 命令行操作进程控制性能优化
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一:命令執行操作
1.通過eval 參數執行指定語句
需要查詢test庫的t1 表中的記錄數有多少,常用方法如下: db.t1.count() 通過命令行eval參數直接執行語句: ./mongo test --eval "printjson(db.t1.count())" ?
2.執行指定文件的內容
如果涉及到很多的操作后,才能得到結果,那么用 eval 的方式來做的話是不可能完成的,
那么更靈活的執行指定文件的方式就派上用場了。例如我們仍然要查看 test 庫 t1 表中的記錄數
user_count.js就是我們要執行的文件,里面的內容如下
[root@localhost bin]# cat user_count.js??
var totalcount = db.user.count();??
printjson('Total count of user is :??? ' + totalcount);??
printjson('-----------------------');
?下面我們將執行這個文件
[root@localhost bin]# ./mongo?user_count.js
MongoDB shell version: 1.8.1
connecting to: test
"Total count of user is : ? ?0"
"-----------------------"
大家可以看到最終得到 t1 表的記錄數 ?0,那么一些不必要的說明性文字我們要是不希望出
現該怎么辦呢??
[root@localhost bin]# ./mongo --quiet ? ??user_count.js
"Total count of t1 is : ? ?0"
"-----------------------"?
?過指定quiet參數,即可以將一些登錄信息屏蔽掉,這樣可以讓結果更清晰。
二:進程控制
1.查看活動進程
便于了解系統正在做什么,以便做下一步判斷?
> db.currentOp();? > //? 等同于: db.$cmd.sys.inprog.findOne()? > db.currentOp();
{
??????? "inprog" : [
??????????????? {
??????????????????????? "desc" : "conn15",
??????????????????????? "threadId" : "1024",
??????????????????????? "connectionId" : 15,
??????????????????????? "opid" : 100,
??????????????????????? "active" : true,
??????????????????????? "secs_running" : 0,
??????????????????????? "microsecs_running" : NumberLong(78),
??????????????????????? "op" : "query",
??????????????????????? "ns" : "admin.$cmd",
??????????????????????? "query" : {?"currentOp" : 1? },
??????????????????????? "client" : "127.0.0.1:55068",
??????????????????????? "numYields" : 0,
??????????????????????? "locks" : {},
??????????????????????? "waitingForLock" : false,
??????????????????????? "lockStats" : {}
??????????????? }
??????? ],
??????? "ok" : 1
}
? 字段說明:
? Opid:? 操作進程號
? Op:? 操作類型(查詢,更新等)
? Ns:? 命名空間,? 指操作的是哪個對象
? Query:? 如果操作類型是查詢的話,這里將顯示具體的查詢內容
? lockType:? 鎖的類型,指明是讀鎖還是寫鎖?
2. 結束進程
如果某個異常是由于某個進程產生的,那么一般 DBA 都會毫不留情的殺掉這個罪魁禍首進程,下面將是這操作?
db.killOp(1234/*opid*/) > //? 等同于: db.$cmd.sys.killop.findOne({op:1234})?
注意: 不要 kill 內部發起的操作,比如說 replica set 發起的 sync 操作等?
三:性能篇
? ?1.基礎索引
MongoDB 提供了多樣性的索引支持,索引信息被保存在system.indexes中,且默認總是為_id創建索引,它的索引使用基本和 MySQL 等關系型數據庫一樣。其實可以這樣說說,索引是凌駕于數據存儲系統之上的另一層系統,所以各種結構迥異的存儲都有相同或相似的索引實現及使用接口并不足為奇。? ?? ?1.1 基礎索引
? 在字段age上創建索引,1(升序);-1(降序)? ?>?db.persion.ensureIndex({age:1})?
{ ??????? "createdCollectionAutomatically" : false, ??????? "numIndexesBefore" : 1, ??????? "numIndexesAfter" : 2, ??????? "ok" : 1 }
>??db.persion.getIndexes(); ? --查看persion的index [ ??????? { ??????????????? "v" : 1, ??????????????? "key" : {?"_id" : 1}, ??????????????? "name" : "_id_", ??????????????? "ns" : "test.persion"}, ??????? { ??????????????? "v" : 1, ??????????????? "key" : {?"age" : 1?}, ??????????????? "name" : "age_1", ??????????????? "ns" : "test.persion" ??????? } ]
當系統已有大量數據時,創建索引就是個非常耗時的活,我們可以在后臺執行,只需指定“backgroud:true”即可。
>?db.persion.ensureIndex({age:1} , {backgroud:true})? { ??????? "createdCollectionAutomatically" : false, ??????? "numIndexesBefore" : 2, ??????? "errmsg" : "Index with name: age_1 already exists with different options", ??????? "code" : 85, ??????? "ok" : 0 }
2.文檔索引
>db.factories.insert( { name: "wwl",?addr: { city: "Beijing", state: "BJ" } } );? //在addr 列上創建索引
>db.factories.ensureIndex( { addr : 1 } );
//下面這個查詢將會用到我們剛剛建立的索引
>db.factories.find( { addr: { city: "Beijing", state: "BJ" } } );
{ "_id" : ObjectId("5549acacf69bb9e7f81e632a"), "name" : "wwl", "addr" : { "city" : "Beijing", "state" : "BJ" } }
//但是下面這個查詢將不會用到索引,因為查詢的順序跟索引建立的順序不一樣 db.factories.find( { addr: { state: "BJ" , city: "Beijing"} } );?
3.組合索引
> db.factories.ensureIndex( { "addr.city" : 1, "addr.state" : 1 } );? //? 下面的查詢都用到了這個索引 >?db.factories.find( { "addr.city" : "Beijing", "addr.state" : "BJ" } );?--ok >?db.factories.find( { "addr.city" : "Beijing" } ); ? ?--ok >?db.factories.find().sort( { "addr.city" : 1, "addr.state" : 1 } );??--ok >?db.factories.find().sort( { "addr.city" : 1 } ) ? ??--ok
4.唯一索引
? ?只需在ensureIndex命令中指定”unique:true”即可創建唯一索引。例如,往表 t4 中插入2 條記錄 ? ?>?db.t4.insert({firstname: "wang1", lastname: "wenlong1"});
? ?>?db.t4.insert({firstname: "wang2", lastname: "wenlong2"});?
? 在t4表中建立唯一索引 ? ? ?> db.t4.ensureIndex({firstname: 1, lastname: 1},?{unique: true});
5.強制使用索引
> db.t5.insert({name: "zw",age: 20}) > db.t5.ensureIndex({name:1, age:1}) >?db.t5.find({age:{$lt:30}}).explain()? { ??????? "queryPlanner" : { ??????????????? "plannerVersion" : 1, ??????????????? "namespace" : "test.t5", ??????????????? "indexFilterSet" : false, ??????????????? "parsedQuery" : {? "age" : {?"$lt" : 30?}}, ??????????????? "winningPlan" : { ??????????????????????? "stage" : "COLLSCAN", ??????????????????????? "filter" : {?"age" : {?"$lt" : 30?}?},? "direction" : "forward"? ?},?"rejectedPlans" : [ ]?}, ??????? "serverInfo" : { ??????????????? "host" : "BR56", ??????????????? "port" : 27017, ??????????????? "version" : "3.1.2", ??????????????? "gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991" ? ? ? ? ? ?}, ??????? "ok" : 1 ? ? ?--沒有出現indexbounds 沒有走索引 }
>db.t5.find({age:{$lt:30}}).hint({name:1, age:1}).explain() ?--強制使用索引 ? { ??????? "queryPlanner" : { ??????????????? "plannerVersion" : 1, ??????????????? "namespace" : "test.t5", ??????????????? "indexFilterSet" : false, ??????????????? "parsedQuery" : { ??????????????????????? "age" : {? "$lt" : 30?}? }, ??????????????? "winningPlan" : { ??????????????????????? "stage" : "KEEP_MUTATIONS", ??????????????????????? "inputStage" : { ??????????????????????????????? "stage" : "FETCH", ??????????????????????????????? "filter" : { ??????????????????????????????????????? "age" : {? ? "$lt" : 30? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}, ??????????????????????????????? "inputStage" : { ??????????????????????????????????????? "stage" : "IXSCAN", ??????????????????????????????????????? "keyPattern" : { ??????????????????????????????????????????????? "name" : 1, ??????????????????????????????????????????????? "age" : 1 ??????????????????????????????????????? }, ??????????????????????????????????????? "indexName" : "name_1_age_1", ??????????????????????????????????????? "isMultiKey" : false, ??????????????????????????????????????? "indexVersion" : 1, ??????????????????????????????????????? "direction" : "forward", ??????????????????????????????????????? "indexBounds" : { ? ? ? ? ???--這里使用索引了 ??????????????????????????????????????????????? "name" : [ ??????????????????????????????????????????????????????? "[MinKey, MaxKey]" ??????????????????????????????????????????????? ], ??????????????????????????????????????????????? "age" : [ ??????????????????????????????????????????????????????? "[MinKey, MaxKey]" ??????????????????????????????????????????????? ] ??????????????????????????????????????? } ??????????????????????????????? } ??????????????????????? } ??????????????? }, ??????????????? "rejectedPlans" : [ ] ??????? }, ??????? "serverInfo" : { ??????????????? "host" : "BR56", ??????????????? "port" : 27017, ??????????????? "version" : "3.1.2", ??????????????? "gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991" ??????? }, ??????? "ok" : 1 }
6.刪除索引
??刪除索引分為刪除某張表的所有索引和刪除某張表的某個索引,具體如下:? ??//刪除 t3表中的所有索引? ? >?db.t3.dropIndexes()? ? ?//刪除 t4表中的 firstname 索引 ? >?db.t4.dropIndex({firstname: 1})?
7.執行計劃
? ??? ?MongoDB? 提供了一個? explain? 命令讓我們獲知系統如何處理查詢請求。利用? explain? 命令,我們可以很好地觀察系統如何使用索引來加快檢索,同時可以針對性優化索引。?
>?db.t5.find({age:{$gt:45}}, {name:1}).explain()
{
??????? "queryPlanner" : {
??????????????? "plannerVersion" : 1,
??????????????? "namespace" : "test.t5",
??????????????? "indexFilterSet" : false,
??????????????? "parsedQuery" : {
??????????????????????? "age" : {
??????????????????????????????? "$gt" : 45
??????????????????????? }
??????????????? },
??????????????? "winningPlan" : {
??????????????????????? "stage" : "PROJECTION",
??????????????????????? "transformBy" : {
??????????????????????????????? "name" : 1
??????????????????????? },
??????????????????????? "inputStage" : {
??????????????????????????????? "stage" : "FETCH",
??????????????????????????????? "inputStage" : {
??????????????????????????????????????? "stage" : "IXSCAN",
??????????????????????????????????????? "keyPattern" : {
??????????????????????????????????????????????? "age" : 1
??????????????????????????????????????? },
??????????????????????????????????????? "indexName" : "age_1",
??????????????????????????????????????? "isMultiKey" : false,
??????????????????????????????????????? "indexVersion" : 1,
??????????????????????????????????????? "direction" : "forward",
??????????????????????????????????????? "indexBounds" : {
??????????????????????????????????????????????? "age" : [
??????????????????????????????????????????????????????? "(45.0, 1.#INF]"
??????????????????????????????????????????????? ]
??????????????????????????????????????? }
??????????????????????????????? }
??????????????????????? }
??????????????? },
??????????????? "rejectedPlans" : [ ]
??????? },
??????? "serverInfo" : {
??????????????? "host" : "BR56",
??????????????? "port" : 27017,
??????????????? "version" : "3.1.2",
??????????????? "gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991"
??????? },
??????? "ok" : 1
}
?字段說明:
? cursor:? 返回游標類型(BasicCursor? 或? BtreeCursor)
? nscanned:? 被掃描的文檔數量
? n:? 返回的文檔數量
? millis:? 耗時(毫秒)
? indexBounds:? 所使用的索引?
8.優化器profile
? 8.1 開啟? Profiling? 功能?
??有兩種方式可以控制? Profiling? 的開關和級別,第一種是直接在啟動參數里直接進行設置。?啟動MongoDB時加上–profile=級別? 即可。 也可以在客戶端調用 db.setProfilingLevel(級別)? 命令來實時配置,Profiler? 信息保存在 system.profile 中。我們可以通過db.getProfilingLevel()命令來獲取當前的Profile級別,類似如下操作 .
>db.setProfilingLevel(2); ?? { "was" : 0, "slowms" : 100, "ok" : 1 }
上面profile的級別可以取0,1,2? 三個值,他們表示的意義如下:
? 0 –? 不開啟
? 1 –? 記錄慢命令? (默認為>100ms)
? 2 –? 記錄所有命令?
Profile? 記錄在級別 1 時會記錄慢命令,那么這個慢的定義是什么?上面我們說到其默認為
100ms,當然有默認就有設置,其設置方法和級別一樣有兩種,一種是通過添加–slowms 啟
動參數配置。第二種是調用db.setProfilingLevel時加上第二個參數:
> db.setProfilingLevel( level , slowms )??? >?db.setProfilingLevel( 1 , 10 );? { "was" : 2, "slowms" : 100, "ok" : 1 }
?8.2 查詢??Profiling 記錄
? ?與 MySQL 的慢查詢日志不同,MongoDB? Profile? 記錄是直接存在系統 db 里的,記錄位置 system.profile? ,所以,我們只要查詢這個 Collection 的記錄就可以獲取到我們的? Profile? 記錄了。
列出執行時間長于某一限度(5ms)的? Profile? 記錄:db.system.profile.find( { millis : { $gt : 5 } } )?
查看最新的? Profile? 記錄:
db.system.profile.find().sort({$natural:-1}).limit(1)
?{? "ts"? :? ISODate("2012-05-20T16:50:36.321Z"), ? ? "info"? :? "query? test.system.profile?? ? ? ?reslen :1219 ? ? ?nscanned:8??? \nquery: { query: {}, orderby: { $natural: -1.0 } }??? ? ? ?nreturned:8 ?bytes:1203", ? ? ?"millis" : 0 ?}?
字段說明 字段說明 字段說明 字段說明: ? ts:? 該命令在何時執行 ? info:? 本命令的詳細信息 ? reslen:? 返回結果集的大小 ??nscanned:? 本次查詢掃描的記錄數 ??nreturned:? 本次查詢實際返回的結果集 ? millis:? 該命令執行耗時,以毫秒記
MongoDB? Shell? 還提供了一個比較簡潔的命令 show? profile,可列出最近5 條執行時間超過 1ms的? Profile? 記錄。
9.性能優化
如果nscanned(掃描的記錄數)遠大于nreturned(返回結果的記錄數)的話,那么我們就要考慮通過加索引來優化記錄定位了。 reslen? 如果過大,那么說明我們返回的結果集太大了,這時請查看find函數的第二個參數是否只寫上了你需要的屬性名。 對于創建索引的建議是 :如果很少讀,那么盡量不要添加索引,因為索引越多,寫操作會越慢。如果讀量很大,那么創建索引還是比較劃算的。?
假設我們按照時間戳查詢最近發表的 10篇博客文章:
articles = db.persion.find().sort({ts:-1}); for (var i=0; i< 10; i++) { ????? print(articles[i].getSummary()); }?
9.1 優化方案一:創建索引
在查詢條件的字段上,或者排序條件的字段上創建索引,可以顯著提高執行效率:? db.posts.ensureIndex({ts:1});
9.2?優化方案二:限定返回結果數
使用limit()限定返回結果集的大小,可以減少database server 的資源消耗,可以減少網絡傳輸數據量。
articles = db.persion.find().sort({ts:-1}).limit(10);?
9.3?優化方案三:只查詢用到的字段
>articles = db.persion.find({}, {age:1}).sort({age:-1}).limit(10);?
{ "_id" : ObjectId("5549aaacf69bb9e7f81e6329"), "age" : 24 } { "_id" : ObjectId("5549aaa0f69bb9e7f81e6328"), "age" : 23 } { "_id" : ObjectId("5549aa97f69bb9e7f81e6327"), "age" : 22 } { "_id" : ObjectId("55472415dd647b87c8344724"), "age" : 20 } { "_id" : ObjectId("55471e5ddd647b87c8344721"), "age" : 18 }
? ? ? ? 注意:如果只查詢部分字段的話,不能用返回的對象直接更新數據庫。下面的代碼是錯誤的
a_post = db.posts.findOne({}, Post.summaryFields); a_post.x = 3; db.posts.save(a_post);?
9.4?優化方案四:cappend collection
capped Collections比普通Collections的讀寫效率高。 Capped Collections是高效率的Collection 類型,它有如下特點:?
1、 固定大小;Capped Collections必須事先創建,并設置大小:? >?db.createCollection("mycoll", {capped:true, size:100000})? 2、 Capped? Collections 可以 insert 和 update 操作;不能delete 操作。只能用 drop()方法刪除整個Collection。 3、 默認基于Insert的次序排序的。如果查詢時沒有排序,則總是按照insert的順序返回。 4、 FIFO。如果超過了 Collection 的限定大小,則用 FIFO 算法,新記錄將替代最先 insert 的記錄。?
9.5?優化方案五:采用Server Side Code Execution ?
?????Server-Side Processing類似于SQL數據庫的存儲過程,使用Server-Side Processing可以減小網絡通訊的開銷。?
9.6?優化方案六:采用hint
一般情況下MongoDB query optimizer 都工作良好,但有些情況下使用hint()可以提高操作效 率。Hint可以強制要求查詢操作使用某個索引。例如,如果要查詢多個字段的值,如果在其 中一個字段上有索引,可以使用hint:?
db.collection.find({user:u, foo:d}).hint({user:1});?
9.6?優化方案七:采用profiling
?profiling 功能肯定是會影響效率的,但是不太嚴重,原因是他使用的是 system.profile? 來記錄,而system.profile? 是一個capped collection? 這種collection? 在操作上有一些限制和特點,但是效率更高。
1.通過eval 參數執行指定語句
需要查詢test庫的t1 表中的記錄數有多少,常用方法如下: db.t1.count() 通過命令行eval參數直接執行語句: ./mongo test --eval "printjson(db.t1.count())" ?
2.執行指定文件的內容
如果涉及到很多的操作后,才能得到結果,那么用 eval 的方式來做的話是不可能完成的,
那么更靈活的執行指定文件的方式就派上用場了。例如我們仍然要查看 test 庫 t1 表中的記錄數
user_count.js就是我們要執行的文件,里面的內容如下
[root@localhost bin]# cat user_count.js??
var totalcount = db.user.count();??
printjson('Total count of user is :??? ' + totalcount);??
printjson('-----------------------');
?下面我們將執行這個文件
[root@localhost bin]# ./mongo?user_count.js
MongoDB shell version: 1.8.1
connecting to: test
"Total count of user is : ? ?0"
"-----------------------"
大家可以看到最終得到 t1 表的記錄數 ?0,那么一些不必要的說明性文字我們要是不希望出
現該怎么辦呢??
[root@localhost bin]# ./mongo --quiet ? ??user_count.js
"Total count of t1 is : ? ?0"
"-----------------------"?
?過指定quiet參數,即可以將一些登錄信息屏蔽掉,這樣可以讓結果更清晰。
二:進程控制
1.查看活動進程
便于了解系統正在做什么,以便做下一步判斷?
> db.currentOp();? > //? 等同于: db.$cmd.sys.inprog.findOne()? > db.currentOp();
{
??????? "inprog" : [
??????????????? {
??????????????????????? "desc" : "conn15",
??????????????????????? "threadId" : "1024",
??????????????????????? "connectionId" : 15,
??????????????????????? "opid" : 100,
??????????????????????? "active" : true,
??????????????????????? "secs_running" : 0,
??????????????????????? "microsecs_running" : NumberLong(78),
??????????????????????? "op" : "query",
??????????????????????? "ns" : "admin.$cmd",
??????????????????????? "query" : {?"currentOp" : 1? },
??????????????????????? "client" : "127.0.0.1:55068",
??????????????????????? "numYields" : 0,
??????????????????????? "locks" : {},
??????????????????????? "waitingForLock" : false,
??????????????????????? "lockStats" : {}
??????????????? }
??????? ],
??????? "ok" : 1
}
? 字段說明:
? Opid:? 操作進程號
? Op:? 操作類型(查詢,更新等)
? Ns:? 命名空間,? 指操作的是哪個對象
? Query:? 如果操作類型是查詢的話,這里將顯示具體的查詢內容
? lockType:? 鎖的類型,指明是讀鎖還是寫鎖?
2. 結束進程
如果某個異常是由于某個進程產生的,那么一般 DBA 都會毫不留情的殺掉這個罪魁禍首進程,下面將是這操作?
db.killOp(1234/*opid*/) > //? 等同于: db.$cmd.sys.killop.findOne({op:1234})?
注意: 不要 kill 內部發起的操作,比如說 replica set 發起的 sync 操作等?
三:性能篇
? ?1.基礎索引
MongoDB 提供了多樣性的索引支持,索引信息被保存在system.indexes中,且默認總是為_id創建索引,它的索引使用基本和 MySQL 等關系型數據庫一樣。其實可以這樣說說,索引是凌駕于數據存儲系統之上的另一層系統,所以各種結構迥異的存儲都有相同或相似的索引實現及使用接口并不足為奇。? ?? ?1.1 基礎索引
? 在字段age上創建索引,1(升序);-1(降序)? ?>?db.persion.ensureIndex({age:1})?
{ ??????? "createdCollectionAutomatically" : false, ??????? "numIndexesBefore" : 1, ??????? "numIndexesAfter" : 2, ??????? "ok" : 1 }
>??db.persion.getIndexes(); ? --查看persion的index [ ??????? { ??????????????? "v" : 1, ??????????????? "key" : {?"_id" : 1}, ??????????????? "name" : "_id_", ??????????????? "ns" : "test.persion"}, ??????? { ??????????????? "v" : 1, ??????????????? "key" : {?"age" : 1?}, ??????????????? "name" : "age_1", ??????????????? "ns" : "test.persion" ??????? } ]
當系統已有大量數據時,創建索引就是個非常耗時的活,我們可以在后臺執行,只需指定“backgroud:true”即可。
>?db.persion.ensureIndex({age:1} , {backgroud:true})? { ??????? "createdCollectionAutomatically" : false, ??????? "numIndexesBefore" : 2, ??????? "errmsg" : "Index with name: age_1 already exists with different options", ??????? "code" : 85, ??????? "ok" : 0 }
2.文檔索引
>db.factories.insert( { name: "wwl",?addr: { city: "Beijing", state: "BJ" } } );? //在addr 列上創建索引
>db.factories.ensureIndex( { addr : 1 } );
//下面這個查詢將會用到我們剛剛建立的索引
>db.factories.find( { addr: { city: "Beijing", state: "BJ" } } );
{ "_id" : ObjectId("5549acacf69bb9e7f81e632a"), "name" : "wwl", "addr" : { "city" : "Beijing", "state" : "BJ" } }
//但是下面這個查詢將不會用到索引,因為查詢的順序跟索引建立的順序不一樣 db.factories.find( { addr: { state: "BJ" , city: "Beijing"} } );?
3.組合索引
> db.factories.ensureIndex( { "addr.city" : 1, "addr.state" : 1 } );? //? 下面的查詢都用到了這個索引 >?db.factories.find( { "addr.city" : "Beijing", "addr.state" : "BJ" } );?--ok >?db.factories.find( { "addr.city" : "Beijing" } ); ? ?--ok >?db.factories.find().sort( { "addr.city" : 1, "addr.state" : 1 } );??--ok >?db.factories.find().sort( { "addr.city" : 1 } ) ? ??--ok
4.唯一索引
? ?只需在ensureIndex命令中指定”unique:true”即可創建唯一索引。例如,往表 t4 中插入2 條記錄 ? ?>?db.t4.insert({firstname: "wang1", lastname: "wenlong1"});
? ?>?db.t4.insert({firstname: "wang2", lastname: "wenlong2"});?
? 在t4表中建立唯一索引 ? ? ?> db.t4.ensureIndex({firstname: 1, lastname: 1},?{unique: true});
5.強制使用索引
> db.t5.insert({name: "zw",age: 20}) > db.t5.ensureIndex({name:1, age:1}) >?db.t5.find({age:{$lt:30}}).explain()? { ??????? "queryPlanner" : { ??????????????? "plannerVersion" : 1, ??????????????? "namespace" : "test.t5", ??????????????? "indexFilterSet" : false, ??????????????? "parsedQuery" : {? "age" : {?"$lt" : 30?}}, ??????????????? "winningPlan" : { ??????????????????????? "stage" : "COLLSCAN", ??????????????????????? "filter" : {?"age" : {?"$lt" : 30?}?},? "direction" : "forward"? ?},?"rejectedPlans" : [ ]?}, ??????? "serverInfo" : { ??????????????? "host" : "BR56", ??????????????? "port" : 27017, ??????????????? "version" : "3.1.2", ??????????????? "gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991" ? ? ? ? ? ?}, ??????? "ok" : 1 ? ? ?--沒有出現indexbounds 沒有走索引 }
>db.t5.find({age:{$lt:30}}).hint({name:1, age:1}).explain() ?--強制使用索引 ? { ??????? "queryPlanner" : { ??????????????? "plannerVersion" : 1, ??????????????? "namespace" : "test.t5", ??????????????? "indexFilterSet" : false, ??????????????? "parsedQuery" : { ??????????????????????? "age" : {? "$lt" : 30?}? }, ??????????????? "winningPlan" : { ??????????????????????? "stage" : "KEEP_MUTATIONS", ??????????????????????? "inputStage" : { ??????????????????????????????? "stage" : "FETCH", ??????????????????????????????? "filter" : { ??????????????????????????????????????? "age" : {? ? "$lt" : 30? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}, ??????????????????????????????? "inputStage" : { ??????????????????????????????????????? "stage" : "IXSCAN", ??????????????????????????????????????? "keyPattern" : { ??????????????????????????????????????????????? "name" : 1, ??????????????????????????????????????????????? "age" : 1 ??????????????????????????????????????? }, ??????????????????????????????????????? "indexName" : "name_1_age_1", ??????????????????????????????????????? "isMultiKey" : false, ??????????????????????????????????????? "indexVersion" : 1, ??????????????????????????????????????? "direction" : "forward", ??????????????????????????????????????? "indexBounds" : { ? ? ? ? ???--這里使用索引了 ??????????????????????????????????????????????? "name" : [ ??????????????????????????????????????????????????????? "[MinKey, MaxKey]" ??????????????????????????????????????????????? ], ??????????????????????????????????????????????? "age" : [ ??????????????????????????????????????????????????????? "[MinKey, MaxKey]" ??????????????????????????????????????????????? ] ??????????????????????????????????????? } ??????????????????????????????? } ??????????????????????? } ??????????????? }, ??????????????? "rejectedPlans" : [ ] ??????? }, ??????? "serverInfo" : { ??????????????? "host" : "BR56", ??????????????? "port" : 27017, ??????????????? "version" : "3.1.2", ??????????????? "gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991" ??????? }, ??????? "ok" : 1 }
6.刪除索引
??刪除索引分為刪除某張表的所有索引和刪除某張表的某個索引,具體如下:? ??//刪除 t3表中的所有索引? ? >?db.t3.dropIndexes()? ? ?//刪除 t4表中的 firstname 索引 ? >?db.t4.dropIndex({firstname: 1})?
7.執行計劃
? ??? ?MongoDB? 提供了一個? explain? 命令讓我們獲知系統如何處理查詢請求。利用? explain? 命令,我們可以很好地觀察系統如何使用索引來加快檢索,同時可以針對性優化索引。?
>?db.t5.find({age:{$gt:45}}, {name:1}).explain()
{
??????? "queryPlanner" : {
??????????????? "plannerVersion" : 1,
??????????????? "namespace" : "test.t5",
??????????????? "indexFilterSet" : false,
??????????????? "parsedQuery" : {
??????????????????????? "age" : {
??????????????????????????????? "$gt" : 45
??????????????????????? }
??????????????? },
??????????????? "winningPlan" : {
??????????????????????? "stage" : "PROJECTION",
??????????????????????? "transformBy" : {
??????????????????????????????? "name" : 1
??????????????????????? },
??????????????????????? "inputStage" : {
??????????????????????????????? "stage" : "FETCH",
??????????????????????????????? "inputStage" : {
??????????????????????????????????????? "stage" : "IXSCAN",
??????????????????????????????????????? "keyPattern" : {
??????????????????????????????????????????????? "age" : 1
??????????????????????????????????????? },
??????????????????????????????????????? "indexName" : "age_1",
??????????????????????????????????????? "isMultiKey" : false,
??????????????????????????????????????? "indexVersion" : 1,
??????????????????????????????????????? "direction" : "forward",
??????????????????????????????????????? "indexBounds" : {
??????????????????????????????????????????????? "age" : [
??????????????????????????????????????????????????????? "(45.0, 1.#INF]"
??????????????????????????????????????????????? ]
??????????????????????????????????????? }
??????????????????????????????? }
??????????????????????? }
??????????????? },
??????????????? "rejectedPlans" : [ ]
??????? },
??????? "serverInfo" : {
??????????????? "host" : "BR56",
??????????????? "port" : 27017,
??????????????? "version" : "3.1.2",
??????????????? "gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991"
??????? },
??????? "ok" : 1
}
?字段說明:
? cursor:? 返回游標類型(BasicCursor? 或? BtreeCursor)
? nscanned:? 被掃描的文檔數量
? n:? 返回的文檔數量
? millis:? 耗時(毫秒)
? indexBounds:? 所使用的索引?
8.優化器profile
? 8.1 開啟? Profiling? 功能?
??有兩種方式可以控制? Profiling? 的開關和級別,第一種是直接在啟動參數里直接進行設置。?啟動MongoDB時加上–profile=級別? 即可。 也可以在客戶端調用 db.setProfilingLevel(級別)? 命令來實時配置,Profiler? 信息保存在 system.profile 中。我們可以通過db.getProfilingLevel()命令來獲取當前的Profile級別,類似如下操作 .
>db.setProfilingLevel(2); ?? { "was" : 0, "slowms" : 100, "ok" : 1 }
上面profile的級別可以取0,1,2? 三個值,他們表示的意義如下:
? 0 –? 不開啟
? 1 –? 記錄慢命令? (默認為>100ms)
? 2 –? 記錄所有命令?
Profile? 記錄在級別 1 時會記錄慢命令,那么這個慢的定義是什么?上面我們說到其默認為
100ms,當然有默認就有設置,其設置方法和級別一樣有兩種,一種是通過添加–slowms 啟
動參數配置。第二種是調用db.setProfilingLevel時加上第二個參數:
> db.setProfilingLevel( level , slowms )??? >?db.setProfilingLevel( 1 , 10 );? { "was" : 2, "slowms" : 100, "ok" : 1 }
?8.2 查詢??Profiling 記錄
? ?與 MySQL 的慢查詢日志不同,MongoDB? Profile? 記錄是直接存在系統 db 里的,記錄位置 system.profile? ,所以,我們只要查詢這個 Collection 的記錄就可以獲取到我們的? Profile? 記錄了。
列出執行時間長于某一限度(5ms)的? Profile? 記錄:db.system.profile.find( { millis : { $gt : 5 } } )?
查看最新的? Profile? 記錄:
db.system.profile.find().sort({$natural:-1}).limit(1)
?{? "ts"? :? ISODate("2012-05-20T16:50:36.321Z"), ? ? "info"? :? "query? test.system.profile?? ? ? ?reslen :1219 ? ? ?nscanned:8??? \nquery: { query: {}, orderby: { $natural: -1.0 } }??? ? ? ?nreturned:8 ?bytes:1203", ? ? ?"millis" : 0 ?}?
字段說明 字段說明 字段說明 字段說明: ? ts:? 該命令在何時執行 ? info:? 本命令的詳細信息 ? reslen:? 返回結果集的大小 ??nscanned:? 本次查詢掃描的記錄數 ??nreturned:? 本次查詢實際返回的結果集 ? millis:? 該命令執行耗時,以毫秒記
MongoDB? Shell? 還提供了一個比較簡潔的命令 show? profile,可列出最近5 條執行時間超過 1ms的? Profile? 記錄。
9.性能優化
如果nscanned(掃描的記錄數)遠大于nreturned(返回結果的記錄數)的話,那么我們就要考慮通過加索引來優化記錄定位了。 reslen? 如果過大,那么說明我們返回的結果集太大了,這時請查看find函數的第二個參數是否只寫上了你需要的屬性名。 對于創建索引的建議是 :如果很少讀,那么盡量不要添加索引,因為索引越多,寫操作會越慢。如果讀量很大,那么創建索引還是比較劃算的。?
假設我們按照時間戳查詢最近發表的 10篇博客文章:
articles = db.persion.find().sort({ts:-1}); for (var i=0; i< 10; i++) { ????? print(articles[i].getSummary()); }?
9.1 優化方案一:創建索引
在查詢條件的字段上,或者排序條件的字段上創建索引,可以顯著提高執行效率:? db.posts.ensureIndex({ts:1});
9.2?優化方案二:限定返回結果數
使用limit()限定返回結果集的大小,可以減少database server 的資源消耗,可以減少網絡傳輸數據量。
articles = db.persion.find().sort({ts:-1}).limit(10);?
9.3?優化方案三:只查詢用到的字段
>articles = db.persion.find({}, {age:1}).sort({age:-1}).limit(10);?
{ "_id" : ObjectId("5549aaacf69bb9e7f81e6329"), "age" : 24 } { "_id" : ObjectId("5549aaa0f69bb9e7f81e6328"), "age" : 23 } { "_id" : ObjectId("5549aa97f69bb9e7f81e6327"), "age" : 22 } { "_id" : ObjectId("55472415dd647b87c8344724"), "age" : 20 } { "_id" : ObjectId("55471e5ddd647b87c8344721"), "age" : 18 }
? ? ? ? 注意:如果只查詢部分字段的話,不能用返回的對象直接更新數據庫。下面的代碼是錯誤的
a_post = db.posts.findOne({}, Post.summaryFields); a_post.x = 3; db.posts.save(a_post);?
9.4?優化方案四:cappend collection
capped Collections比普通Collections的讀寫效率高。 Capped Collections是高效率的Collection 類型,它有如下特點:?
1、 固定大小;Capped Collections必須事先創建,并設置大小:? >?db.createCollection("mycoll", {capped:true, size:100000})? 2、 Capped? Collections 可以 insert 和 update 操作;不能delete 操作。只能用 drop()方法刪除整個Collection。 3、 默認基于Insert的次序排序的。如果查詢時沒有排序,則總是按照insert的順序返回。 4、 FIFO。如果超過了 Collection 的限定大小,則用 FIFO 算法,新記錄將替代最先 insert 的記錄。?
9.5?優化方案五:采用Server Side Code Execution ?
?????Server-Side Processing類似于SQL數據庫的存儲過程,使用Server-Side Processing可以減小網絡通訊的開銷。?
9.6?優化方案六:采用hint
一般情況下MongoDB query optimizer 都工作良好,但有些情況下使用hint()可以提高操作效 率。Hint可以強制要求查詢操作使用某個索引。例如,如果要查詢多個字段的值,如果在其 中一個字段上有索引,可以使用hint:?
db.collection.find({user:u, foo:d}).hint({user:1});?
9.6?優化方案七:采用profiling
?profiling 功能肯定是會影響效率的,但是不太嚴重,原因是他使用的是 system.profile? 來記錄,而system.profile? 是一個capped collection? 這種collection? 在操作上有一些限制和特點,但是效率更高。
總結
以上是生活随笔為你收集整理的第四章:mongodb 命令行操作进程控制性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每日一题(34)—— 线程
- 下一篇: AutoForm Master v4.0