亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MongoDB > 正文

深入理解MongoDB的復合索引

2020-03-14 12:50:32
字體:
來源:轉載
供稿:網友

為什么需要索引

當你抱怨MongoDB集合查詢效率低的時候,可能你就需要考慮使用索引了,為了方便后續介紹,先科普下MongoDB里的索引機制(同樣適用于其他的數據庫比如mysql)。

mongo-9552:PRIMARY> db.person.find(){ "_id" : ObjectId("571b5da31b0d530a03b3ce82"), "name" : "jack", "age" : 19 }{ "_id" : ObjectId("571b5dae1b0d530a03b3ce83"), "name" : "rose", "age" : 20 }{ "_id" : ObjectId("571b5db81b0d530a03b3ce84"), "name" : "jack", "age" : 18 }{ "_id" : ObjectId("571b5dc21b0d530a03b3ce85"), "name" : "tony", "age" : 21 }{ "_id" : ObjectId("571b5dc21b0d530a03b3ce86"), "name" : "adam", "age" : 18 }

當你往某各個集合插入多個文檔后,每個文檔在經過底層的存儲引擎持久化后,會有一個位置信息,通過這個位置信息,就能從存儲引擎里讀出該文檔。比如mmapv1引擎里,位置信息是『文件id + 文件內offset 』, 在wiredtiger存儲引擎(一個KV存儲引擎)里,位置信息是wiredtiger在存儲文檔時生成的一個key,通過這個key能訪問到對應的文檔;為方便介紹,統一用pos(position的縮寫)來代表位置信息。

什么是復合索引

復合索引,即Compound Index,指的是將多個鍵組合到一起創建索引,這樣可以加速匹配多個鍵的查詢。不妨通過一個簡單的示例理解復合索引。

students集合如下:

db.students.find().pretty(){ "_id" : ObjectId("5aa7390ca5be7272a99b042a"), "name" : "zhang", "age" : "15"}{ "_id" : ObjectId("5aa7393ba5be7272a99b042b"), "name" : "wang", "age" : "15"}{ "_id" : ObjectId("5aa7393ba5be7272a99b042c"), "name" : "zhang", "age" : "14"}

在name和age兩個鍵分別創建了索引(_id自帶索引):

db.students.getIndexes()[ { "v" : 1, "key" : { "name" : 1 }, "name" : "name_1", "ns" : "test.students" }, { "v" : 1, "key" : { "age" : 1 }, "name" : "age_1", "ns" : "test.students" }]

當進行多鍵查詢時,可以通過explian()分析執行情況(結果僅保留winningPlan):

db.students.find({name:"zhang",age:"14"}).explain()"winningPlan":{ "stage": "FETCH", "filter": {  "name":  {   "$eq": "zhang"  } }, "inputStage": {  "stage": "IXSCAN",  "keyPattern":  {   "age": 1  },  "indexName": "age_1",  "isMultiKey": false,  "isUnique": false,  "isSparse": false,  "isPartial": false,  "indexVersion": 1,  "direction": "forward",  "indexBounds":  {   "age": [    "[/"14/", /"14/"]"   ]  } }}

由winningPlan可知,這個查詢依次分為IXSCAN和FETCH兩個階段。IXSCAN即索引掃描,使用的是age索引;FETCH即根據索引去查詢文檔,查詢的時候需要使用name進行過濾。

為name和age創建復合索引:

db.students.createIndex({name:1,age:1})db.students.getIndexes()[ { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "name" : "name_1_age_1", "ns" : "test.students" }]

有了復合索引之后,同一個查詢的執行方式就不同了:

db.students.find({name:"zhang",age:"14"}).explain()"winningPlan":{ "stage": "FETCH", "inputStage": {  "stage": "IXSCAN",  "keyPattern":  {   "name": 1,   "age": 1  },  "indexName": "name_1_age_1",  "isMultiKey": false,  "isUnique": false,  "isSparse": false,  "isPartial": false,  "indexVersion": 1,  "direction": "forward",  "indexBounds":  {   "name": [    "[/"zhang/", /"zhang/"]"   ],   "age": [    "[/"14/", /"14/"]"   ]  } }}

由winningPlan可知,這個查詢的順序沒有變化,依次分為IXSCAN和FETCH兩個階段。但是,IXSCAN使用的是name與age的復合索引;FETCH即根據索引去查詢文檔,不需要過濾。

這個示例的數據量太小,并不能看出什么問題。但是實際上,當數據量很大,IXSCAN返回的索引比較多時,FETCH時進行過濾將非常耗時。接下來將介紹一個真實的案例。

定位MongoDB性能問題

隨著接收的錯誤數據不斷增加,我們Fundebug已經累計處理3.5億錯誤事件,這給我們的服務不斷帶來性能方面的挑戰,尤其對于MongoDB集群來說。

對于生產數據庫,配置profile,可以記錄MongoDB的性能數據。執行以下命令,則所有超過1s的數據庫讀寫操作都會被記錄下來。

db.setProfilingLevel(1,1000)

查詢profile所記錄的數據,會發現events集合的某個查詢非常慢:

db.system.profile.find().pretty(){ "op" : "command", "ns" : "fundebug.events", "command" : { "count" : "events", "query" : { "createAt" : { "$lt" : ISODate("2018-02-05T20:30:00.073Z") }, "projectId" : ObjectId("58211791ea2640000c7a3fe6") } }, "keyUpdates" : 0, "writeConflicts" : 0, "numYield" : 1414, "locks" : { "Global" : { "acquireCount" : { "r" : NumberLong(2830) } }, "Database" : { "acquireCount" : { "r" : NumberLong(1415) } }, "Collection" : { "acquireCount" : { "r" : NumberLong(1415) } } }, "responseLength" : 62, "protocol" : "op_query", "millis" : 28521, "execStats" : { }, "ts" : ISODate("2018-03-07T20:30:59.440Z"), "client" : "192.168.59.226", "allUsers" : [ ], "user" : ""}

events集合中有數億個文檔,因此count操作比較慢也不算太意外。根據profile數據,這個查詢耗時28.5s,時間長得有點離譜。另外,numYield高達1414,這應該就是操作如此之慢的直接原因。根據MongoDB文檔,numYield的含義是這樣的:

The number of times the operation yielded to allow other operations to complete. Typically, operations yield when they need access to data that MongoDB has not yet fully read into memory. This allows other operations that have data in memory to complete while MongoDB reads in data for the yielding operation.

這就意味著大量時間消耗在讀取硬盤上,且讀了非常多次??梢酝茰y,應該是索引的問題導致的。

不妨使用explian()來分析一下這個查詢(僅保留executionStats):

db.events.explain("executionStats").count({"projectId" : ObjectId("58211791ea2640000c7a3fe6"),createAt:{"$lt" : ISODate("2018-02-05T20:30:00.073Z")}})"executionStats":{ "executionSuccess": true, "nReturned": 20853, "executionTimeMillis": 28055, "totalKeysExamined": 28338, "totalDocsExamined": 28338, "executionStages": {  "stage": "FETCH",  "filter":  {   "createAt":   {    "$lt": ISODate("2018-02-05T20:30:00.073Z")   }  },  "nReturned": 20853,  "executionTimeMillisEstimate": 27815,  "works": 28339,  "advanced": 20853,  "needTime": 7485,  "needYield": 0,  "saveState": 1387,  "restoreState": 1387,  "isEOF": 1,  "invalidates": 0,  "docsExamined": 28338,  "alreadyHasObj": 0,  "inputStage":  {   "stage": "IXSCAN",   "nReturned": 28338,   "executionTimeMillisEstimate": 30,   "works": 28339,   "advanced": 28338,   "needTime": 0,   "needYield": 0,   "saveState": 1387,   "restoreState": 1387,   "isEOF": 1,   "invalidates": 0,   "keyPattern":   {    "projectId": 1   },   "indexName": "projectId_1",   "isMultiKey": false,   "isUnique": false,   "isSparse": false,   "isPartial": false,   "indexVersion": 1,   "direction": "forward",   "indexBounds":   {    "projectId": [     "[ObjectId('58211791ea2640000c7a3fe6'), ObjectId('58211791ea2640000c7a3fe6')]"    ]   },   "keysExamined": 28338,   "dupsTested": 0,   "dupsDropped": 0,   "seenInvalidated": 0  } }}

可知,events集合并沒有為projectId與createAt建立復合索引,因此IXSCAN階段采用的是projectId索引,其nReturned為28338; FETCH階段需要根據createAt進行過濾,其nReturned為20853,過濾掉了7485個文檔;另外,IXSCAN與FETCH階段的executionTimeMillisEstimate分別為30ms和27815ms,因此基本上所有時間都消耗在了FETCH階段,這應該是讀取硬盤導致的。

創建復合索引

沒有為projectId和createAt創建復合索引是個尷尬的錯誤,趕緊補救一下:

db.events.createIndex({projectId:1,createTime:-1},{background: true})

在生產環境構建索引這種事最好是晚上做,這個命令一共花了大概7個小時吧!background設為true,指的是不要阻塞數據庫的其他操作,保證數據庫的可用性。但是,這個命令會一直占用著終端,這時不能使用CTRL + C,否則會終止索引構建過程。

復合索引創建成果之后,前文的查詢就快了很多(僅保留executionStats):

db.javascriptevents.explain("executionStats").count({"projectId" : ObjectId("58211791ea2640000c7a3fe6"),createAt:{"$lt" : ISODate("2018-02-05T20:30:00.073Z")}})"executionStats":{ "executionSuccess": true, "nReturned": 0, "executionTimeMillis": 47, "totalKeysExamined": 20854, "totalDocsExamined": 0, "executionStages": {  "stage": "COUNT",  "nReturned": 0,  "executionTimeMillisEstimate": 50,  "works": 20854,  "advanced": 0,  "needTime": 20853,  "needYield": 0,  "saveState": 162,  "restoreState": 162,  "isEOF": 1,  "invalidates": 0,  "nCounted": 20853,  "nSkipped": 0,  "inputStage":  {   "stage": "COUNT_SCAN",   "nReturned": 20853,   "executionTimeMillisEstimate": 50,   "works": 20854,   "advanced": 20853,   "needTime": 0,   "needYield": 0,   "saveState": 162,   "restoreState": 162,   "isEOF": 1,   "invalidates": 0,   "keysExamined": 20854,   "keyPattern":   {    "projectId": 1,    "createAt": -1   },   "indexName": "projectId_1_createTime_-1",   "isMultiKey": false,   "isUnique": false,   "isSparse": false,   "isPartial": false,   "indexVersion": 1  } }}

可知,count操作使用了projectId和createAt的復合索引,因此非???,只花了46ms,性能提升了將近600倍?。?!對比使用復合索引前后的結果,發現totalDocsExamined從28338降到了0,表示使用復合索引之后不再需要去查詢文檔,只需要掃描索引就好了,這樣就不需要去訪問磁盤了,自然快了很多。

參考

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到MongoDB頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久国产精品视频| www.欧美三级电影.com| 久热精品在线视频| 国产91ⅴ在线精品免费观看| 91久久久久久国产精品| 亚洲天堂2020| 成人黄色影片在线| 亚洲国产天堂久久综合网| 欧美韩国理论所午夜片917电影| 超碰精品一区二区三区乱码| 日韩资源在线观看| 日韩成人在线电影网| 久久男人资源视频| 日日狠狠久久偷偷四色综合免费| 亚洲偷欧美偷国内偷| 黑人巨大精品欧美一区二区一视频| 美女福利视频一区| 久久精品成人欧美大片古装| 国产一区二区三区高清在线观看| 亚洲国产成人精品一区二区| 日韩高清av一区二区三区| 在线精品国产欧美| 国产精品久久久久av免费| 欧美日本国产在线| 日本精品性网站在线观看| 日韩av综合网站| 久久精品在线播放| 亚洲免费伊人电影在线观看av| 亚洲成色777777在线观看影院| 久久综合电影一区| 亚洲天堂av综合网| 亚洲国产精品电影在线观看| 国产精品视频男人的天堂| 国内精品久久影院| 亚洲视频自拍偷拍| 欧美日韩一区二区三区| 欧美日韩国产精品| 亚洲最大成人免费视频| 久久久91精品| 中国china体内裑精亚洲片| 欧美激情性做爰免费视频| 欧美激情啊啊啊| 欧美激情极品视频| 亚洲天堂日韩电影| 成人av在线网址| 精品国产欧美一区二区五十路| 一本久久综合亚洲鲁鲁| 日韩成人黄色av| 国产一区二区三区直播精品电影| 亚洲人成77777在线观看网| 日av在线播放中文不卡| 亚洲激情在线观看视频免费| 日韩av电影手机在线观看| 欧美在线视频网| 欧美刺激性大交免费视频| 欧美另类xxx| 在线一区二区日韩| 亚洲跨种族黑人xxx| 欧美激情在线观看视频| 欧洲精品毛片网站| 久久国产精品久久久久久久久久| 亚洲天堂av网| 欧美日韩成人在线播放| 欧美日韩国产限制| 九九久久久久久久久激情| 国产日韩在线视频| 亚洲精品国精品久久99热一| 日韩欧美国产免费播放| 国产不卡视频在线| 精品亚洲国产成av人片传媒| 91精品视频网站| 国产精品一区=区| 国产不卡av在线免费观看| 青草青草久热精品视频在线观看| 青青精品视频播放| 色噜噜亚洲精品中文字幕| 亚洲天堂2020| 精品成人av一区| 成人欧美一区二区三区在线湿哒哒| 精品国产一区二区三区在线观看| 91精品国产高清自在线看超| 亚洲精品欧美日韩专区| 亚洲字幕在线观看| 中文字幕亚洲欧美一区二区三区| 国产精品欧美激情在线播放| 日韩欧美极品在线观看| 日韩在线视频观看正片免费网站| 亚洲无av在线中文字幕| 茄子视频成人在线| 国产精品成久久久久三级| 欧美日韩国产精品一区二区三区四区| 久久亚洲春色中文字幕| 亚洲精品第一页| 国产精品夜色7777狼人| 欧美日韩aaaa| 国产成一区二区| 青青草原一区二区| 日日狠狠久久偷偷四色综合免费| 另类专区欧美制服同性| 亚洲电影免费观看高清完整版在线| 亚洲a一级视频| 欧美在线视频网站| 亚洲精品www久久久久久广东| 久久国产视频网站| 久久99久久99精品免观看粉嫩| 久久精品亚洲94久久精品| 97久久精品国产| 欧美精品九九久久| 欧美亚洲另类在线| 亚洲精品视频网上网址在线观看| 久久精品国产91精品亚洲| 亚洲综合色av| 色偷偷偷综合中文字幕;dd| 亚洲一区二区精品| 国产91精品久久久久久久| 国产99在线|中文| 久久久久www| 亚洲色图狂野欧美| 日韩欧美高清视频| 国产精品永久免费| 亚洲国产精彩中文乱码av在线播放| 精品成人久久av| 欧美激情亚洲综合一区| 久久影视三级福利片| 国产精品国模在线| 精品人伦一区二区三区蜜桃网站| 亚洲国产成人精品久久久国产成人一区| 国产91热爆ts人妖在线| 欧美国产中文字幕| 欧美激情区在线播放| 亚洲图片制服诱惑| 久久久久在线观看| 国产欧美韩国高清| 国产精品国产三级国产aⅴ9色| 欧美另类极品videosbest最新版本| 国产精品99久久久久久白浆小说| 日韩免费av片在线观看| 一区二区成人精品| 亚洲男人天堂网| 亚洲色图25p| 日韩中文字幕久久| 成人av在线天堂| 97视频在线观看免费高清完整版在线观看| 亚洲电影在线看| 一区二区三区回区在观看免费视频| 国产成人精品综合| 成人免费xxxxx在线观看| 海角国产乱辈乱精品视频| 孩xxxx性bbbb欧美| 在线视频国产日韩| 日韩视频永久免费观看| 欧美怡红院视频一区二区三区| 亚洲另类激情图| 亚洲摸下面视频| 精品中文字幕在线| 日韩精品极品在线观看播放免费视频| 亚洲黄页网在线观看| 69久久夜色精品国产69乱青草| 欧美人交a欧美精品| 91精品国产91久久久久久最新| 国产亚洲欧洲黄色| 欧美激情国产日韩精品一区18| 亚洲精品女av网站| 日韩最新中文字幕电影免费看|