国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院

首頁 > 數據庫 > MongoDB > 正文

深入理解MongoDB的復合索引

2020-10-29 18:44:01
字體:
來源:轉載
供稿:網友

為什么需要索引?

當你抱怨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.

這就意味著大量時間消耗在讀取硬盤上,且讀了非常多次。可以推測,應該是索引的問題導致的。

不妨使用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,表示使用復合索引之后不再需要去查詢文檔,只需要掃描索引就好了,這樣就不需要去訪問磁盤了,自然快了很多。

參考

  • MongoDB 復合索引
  • MongoDB文檔:Compound Indexes

總結

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院
国产三区在线观看| 九九在线观看免费视频| 欧美日韩在线视频免费观看| 国产黄色一级片| 国产精品视频流白浆免费视频| 最新超碰在线| 国产精品视频二区三区| 国产高清视频在线观看| 国产精品剧情一区二区在线观看| 天堂在线视频| 2019天天操夜夜操| 五月婷婷视频在线观看| 国产精品黄页网站在线播放免费 | 青青久草在线| 88av在线| 蜜桃av在线免费观看| 中文av在线播放| 黄色片视频在线观看| 国产一级免费黄色片| 2019中文字幕在线电影免费| 亚洲精品天堂在线观看| 精品国产一区二区三区不卡在线 | 中文字幕在线视频观看| 国产激情自拍| 天天操天天曰| 99福利在线| 国产区卡一卡二卡三乱码免费| 国产黄在线播放| 久草在线视频网| 国产精品久久久久白浆| 久热久精久品这里在线观看| 91www在线观看| 国产另类图片| 欧美激情福利视频在线观看免费| 国产精品久久久久一区二区国产| 国产在线二区| 91中文字幕| 国产一级激情| 久久精品视频观看| a视频在线观看免费| 99热免费观看| 在线视频观看国产| 精品无人乱码| 99热免费观看| 亚洲精品影视在线| 日本视频一二三区中文字幕| 精品欧美色视频网站在线观看| 国产农村av| 91超碰在线免费| 91在线中文| 国产精品伦一区二区三区视频 | 国产一区二区三区美女秒播| 久久精品蜜桃| 成人免费一区二区三区视频网站| www.av在线播放| 九九免费视频| 最新中文字幕av专区| 天天操人人干| 国产福利图片| 碰草在线视频| 中文字幕国产视频| 国产变态拳头交视频一区二区 | 天天艹天天操| 欧美日韩国产亚洲沙发| www中文字幕在线观看| xxxxx中文字幕| 天堂在线免费观看| 91视频久色| 精品资源在线看| 国产经典自拍视频在线观看 | 狠狠色丁香婷婷| 国产一级黄色片免费| 国产无套粉嫩白浆在线2022年 | 天堂网中文在线| 天天爱天天色| 亚洲人成影院在线| 国产视频一二三区| 看成年女人免费午夜视频| 国产日产一区二区| 男女午夜视频在线观看| 69视频在线观看| 成年网在线观看免费观看网址| 精品一区二区三区在线观看l| 国产中文伊人| 69久久精品| 中文字幕第一页在线| 日本久久国产| 在线观看精品一区二区三区| 国产亚洲精品自在线观看| 日本不卡影院| 亚洲videos| 香蕉视频在线观看www| 九九在线视频| 国产在线视频网站| 国产美女福利在线| 国产中文字幕网| 人成在线免费视频| 免费看黄视频网站| 免费黄色网页在线观看| a视频在线播放| 99在线视频影院| 国产福利微拍精品一区二区| 国产精品一区二区婷婷| 欧洲亚洲精品视频| www.狠狠艹| 豆国产97在线|亚洲| 天堂资源在线中文| 中文字幕不卡免费视频| 最新黄网在线观看| 91麻豆福利| a视频免费看| 麻豆av电影在线观看| 国产男女无套在线播放| 最新黄网在线观看| 国产变态拳头交视频一区二区| 国产一级片在线播放| 国产秀色在线www免费观看| 国产卡1卡2卡三卡在线| 国产一二区在线观看| 国产精品免费麻豆入口| 伊人资源视频在线| 亚洲久草视频| 丁香婷婷在线观看| 一区二区三区四区在线免费视频| 亚洲欧美国产另类首页| 黄网址在线播放免费| 国产经典av| 国产在线三区| 樱花草在线观看www| 国产在线精品一区二区不卡| 国产偷激情在线| 四虎成年永久免费网站| 18加网站在线| 天堂网中文在线| 中文字幕毛片| 性色视频在线| 国产盗摄一区二区| 免费在线看v| 精品国产免费第一区二区| 欧美色第一页| 精品999视频| 五月婷婷丁香激情| 国产69精品久久app免费版| 久久久久久日本一区99| 国产超碰在线| 天堂中文字幕在线| www.毛片| 日本一二三区视频免费高清| 91啦中文在线| 国产午夜电影| 国产欧美黑人| 18成年在线观看| 国产男女av| 日本成人在线播放| 性网站在线播放| 精品推荐蜜桃传媒| 超碰在线国产| 在线看黄色av| 9999在线视频| 91麻豆精品国产91久久| 国产亚洲精品久久久网站好莱 | 亚洲字幕成人中文在线观看| 国产激情自拍| 国产黄在线播放| 精品麻豆视频| av在线你懂的| 超碰国产在线观看| 国产女人在线观看| 老司机在线视频二区| 四虎一区二区三区| 日本福利在线观看| 国产剧情av在线| 91福利在线免费| 国产在线你懂得| 国产乱精品一区二区三区| 国产中文字幕在线看| 国产尤物视频在线| 精品成人免费自拍视频| 国产福利小视频在线| 99reav| av在线二区| 九九热免费在线视频| 国产一区二区三区四区尤物| 国产极品视频| 国产在线一区二区视频| 国产在线精品一区二区不卡| 中文字幕在线影院| 在线观看的av| 国产精品自产拍在线网站| 欧美精品另类| 国产wwww| 超碰免费在线| 国产夫妻视频| 欧美卡一卡二| 2018狠狠干| 久久91精品视频| 2020国产在线视频| 欧美日韩视频精品一区二区| 中文字幕在线免费看| 亚洲精品白浆|