好湿?好紧?好多水好爽自慰,久久久噜久噜久久综合,成人做爰A片免费看黄冈,机机对机机30分钟无遮挡

主頁 > 知識庫 > 深入理解MongoDB的復合索引

深入理解MongoDB的復合索引

熱門標簽:怎么投訴地圖標注 廣州長安公司怎樣申請400電話 蘋果汽車租賃店地圖標注 杭州人工電銷機器人價格 濟南電銷機器人加盟公司 電銷機器人是什么軟件 老虎洗衣店地圖標注 云南外呼系統 呼和浩特電銷外呼系統加盟

為什么需要索引?

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

mongo-9552:PRIMARYgt; 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

總結

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

您可能感興趣的文章:
  • MongoDB索引使用詳解
  • MongoDB中唯一索引(Unique)的那些事
  • MongoDB的基礎查詢和索引操作方法總結
  • MongoDB中創建索引需要注意的事項
  • MongoDB性能篇之創建索引,組合索引,唯一索引,刪除索引和explain執行計劃
  • mongodb處理中文索引與查找字符串詳解
  • MongoDB查詢字段沒有創建索引導致的連接超時異常解案例分享
  • 關于MongoDB索引管理-索引的創建、查看、刪除操作詳解
  • MongoDB自動刪除過期數據的方法(TTL索引)
  • 關于對MongoDB索引的一些簡單理解

標簽:廈門 遼陽 雞西 玉林 無錫 興安盟 自貢 泰安

巨人網絡通訊聲明:本文標題《深入理解MongoDB的復合索引》,本文關鍵詞  深入,理解,MongoDB,的,復合,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《深入理解MongoDB的復合索引》相關的同類信息!
  • 本頁收集關于深入理解MongoDB的復合索引的相關信息資訊供網民參考!
  • 推薦文章
    主站蜘蛛池模板: 图书馆的秘密+全部都是你| 97人人爽人人爽乱码Av国产| 一级大片免费看| 被触手强制扒开侵犯漫画男男| 亚洲国产精品无码一区二区白鹿| 吞精囗交69激情欧美| 宝贝嗯嗯| 别揉我胸啊嗯上课呢小| 韩国r级无删减2023最新电影| 台湾艳女十八式三级| 51精品国自产在线| 无码日韩精品一区二区免费漫画| 欧美另类videosgrstv老| japanesexxxxx动漫片| 日本19禁啪啪免费观看www| 国产精品26uuu观看| 妻子的视频完整版韩国| 黄色小说在线观看视频| 双性受被教官按在寝室狂到腿软| 韩国青草自慰喷水无码av直播间| 白嫩的极品女asspie| 老司机精品在线视频| 亂伦HDWWW欧美BBw另类| 1000部拍拍拍1000部手机版 | 俄罗斯女人与公拘i交酡i视频| 小琪老师电影免费播放西瓜| 日日久久一区二区三区| 欧美性影视| 布衣3d精华布衣123456| 久9视频这里只有精品8| 日韩欧美国产在线一区二区三区| 多人交换性69乐园| 911人人妻人人澡人人爽人人学生| 欧美猛烈性xbxbxbxb| 歪歪漫画sss免费| 亚洲欧洲国产成人综合一本| 婬欲婬香婬色婬视频在线| 善良的小峓子HD中字3D漫画| 耽美小肉文| 无遮挡毛片a级武则天| 青草视频免费在线观看|