1. 程式人生 > >深入理解MongoDB的複合索引

深入理解MongoDB的複合索引

 

 更新時間:2018年03月26日 10:17:37   作者:Fundebug    我要評論

對於MongoDB的多鍵查詢,建立複合索引可以有效提高效能。這篇文章主要給大家介紹了關於MongoDB複合索引的相關資料,文中通過示例程式碼介紹的非常詳細,對大家的學習  

為什麼需要索引?

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

?
1 2 3 4 5 6 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集合如下:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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自帶索引):

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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):

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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建立複合索引:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 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"   } ]

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

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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的資料庫讀寫操作都會被記錄下來。

?
1 db.setProfilingLevel(1,1000)

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

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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):

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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"

相關推薦

深入理解MongoDB複合索引

   更新時間:2018年03月26日 10:17:37   作者:Fundebug    我要評論 對於MongoDB的多鍵查詢,建立複合索引可以有效提高效能。這篇文章主要給大家介紹了關於MongoDB複合

從資料結構(樹)深入理解資料庫的索引

樹 二叉樹 性質: 1. 在非空二叉樹中,第ii層的結點總數不超過2i−12i−1, i>=1i>=1; 2. 深度為hh的二叉樹最多有2h−12h−1個結點(h>=1),最少有h個結點; 3. 對於任意一棵二叉樹,如果其葉結點數為N

ELasticSearch 深入理解系列5 -索引、型別

1 索引含義: 1.1 在ES中索引包含兩層意思:一種是名詞:類似傳統資料庫的庫,一種是動詞:將資料儲存的行為。 1.2 下面是常用的將ES一些概念和資料庫進行對比圖: Relational DB -> Databases -> Tables ->

「生產事故」MongoDB複合索引引發的災難

## 前情提要 1. 11月末`我司商品服務`的`MongoDB主庫`曾出現過嚴重抖動、頻繁鎖庫等情況。 2. 由於諸多業務存在插入`MongoDB`、然後立即查詢等邏輯,因此專案並未開啟讀寫分離。 3. 最終定位問題是由於:伺服器自身磁碟 + 大量`慢查詢`導致 4. 基於上述情況,運維同學後續著重增強了

深入理解mysql索引

bit 大於 ... image mysql 行數據 查詢 數據檢索 強制 建立索引的優缺點: 為什麽要創建索引呢? 這是因為,創建索引可以大大提高系統的性能。 第一、通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。 第二、可

『PyTorch』第五彈_深入理解Tensor對象_中上:索引

nbsp 使用 gpo 簡單 深入理解 pre 需要 sub ext 一、普通索引 示例 a = t.Tensor(4,5) print(a) print(a[0:1,:2]) print(a[0,:2]) # 註意和前一種索引出來的值相同,shape不同 print(

深入理解mysql資料庫B+樹索引

索引的作用: 首先索引通俗來講就像書的目錄,通過索引可以快速查詢對應資料,但這僅僅是表面上的,索引主要作用有3點,這僅僅算作其中1點。以下是鄙人的理解: 通過索引可以減少資料的掃描量(例如上面提到的將全書掃描,變成了根據目錄找) 索引可以把對硬碟的隨機IO變為順序IO()

深入理解Mysql》之利用索引排序

使用Mysql的索引進行排序可以大大提高Mysql的效能,使用索引排序的原則是 按照建立索引的順序查詢和排序,執行如下sql create table t1( id int not null auto_increment, uname varchar(32) not null,

深入理解ElasticSearch(八):索引管理

索引管理 1、建立一個索引 到目前為止, 我們已經通過索引一篇文件建立了一個新的索引 。這個索引採用的是預設的配置,新的欄位通過動態對映的方式被新增到型別對映。現在我們需要對這個建立索引的過程做更多的控制:我們想要確保這個索引有數量適中的主分片,並且在我們索引任何資料 之

深入理解資料庫索引採用B樹和B+樹的原因

         前面幾篇關於資料庫底層磁碟檔案讀取,資料庫索引實現細節進行了深入的研究,但是沒有串聯起來的講解為什麼資料庫索引會採用B樹和B+樹而不是其他的資料結構,例如平衡二叉樹、連結串列等,因此,本文打算從資料庫檔案儲存以及讀取說起,講解資料庫索引的由來。      

深入理解MySQL索引底層資料結構與演算法

一 理解索引的特性 索引是幫助MySQL高效獲取資料的排好序的資料結構 索引儲存在檔案裡 二 索引的各種儲存結構及其優缺點 在開始講這一小節之前,我們先來看一下在資料庫沒有加索引的情況下,SQL中

索引深入理解索引提高查詢速度的原因

1.索引是什麼index索引是幫助資料庫高效獲取資料的資料結構。  是1種資料結構 2.引入索引無論是資料庫查詢資料,還是其他的程式查詢資料利用到的查詢資料必定涉及到相關的查詢演算法。引入:如資料結構的順序表中獲取順序資料一般地我們採用for迴圈來查詢資料此演算法的複雜度為

深入理解Mysql索引的底層資料結構 B+ Tree (2)

  sql查詢 explain的詳細用法 操作時間:尋道時間+旋轉時間 引入索引:採用二叉樹結構 把第二列做為索引生成二叉樹結構,此時查詢89 只做了兩次io操作 但是mysql 為什麼不用二叉樹作為底層索引結構? 紅黑樹 hash where col1 > 6

Oracle 複合索引理解

1、索引相當於書的目錄,目錄越多效率越低,如果目錄很大,那還不如不要目錄,直接全表掃描,這就是為什麼有時有索引,但sql優化器不去用而是全表掃描的原因 2、複合索引,就是目錄太多,又有了目錄的目錄 3、複合索引,在where裡用到複合索引的第一個欄位才會使用此索引 4、如果

mongodb系列(二)使用複合索引中要注意欄位的前後

背景 預先建立了一個複合索引,分別以 updated_at 和 size 兩個欄位作為索引依據,其中該collection有94萬+個document db.cms_resources.createIndex({updated_at:-1,size:-1},{name:'

深入理解MySQL索引原理和實現——為什麼索引可以加速查詢?

說到索引,很多人都知道“索引是一個排序的列表,在這個列表中儲存著索引的值和包含這個值的資料所在行的實體地址,在資料十分龐大的時候,索引可以大大加快查詢的速度,這是因為使用索引後可以不用掃描全表來定位某行的資料,而是先通過索引表找到該行資料對應的實體地址然後訪問相應的資料。”但

Mysql全文索引之-深入理解原理

我們前面介紹過mysql全文檢索的一個外掛Onesql,瞭解了全文檢索其實Mysql的Innodb引擎預設也是支援全文檢索的,只支援英文。其背後的原理都是倒排索引本文預設Mysql支援的全文檢索倒排索引 倒排索引跟B+樹一樣,也是一種資料結構。一般利用關聯陣列,在輔助表中儲存

mysql索引深入理解

詳解b+樹 如上圖,是一顆b+樹,關於b+樹的定義可以參見B+樹,這裡只說一些重點,淺藍色的塊我們稱之為一個磁碟塊,可以看到每個磁碟塊包含幾個資料項(深藍色所示)和指標(黃色所示),如磁碟塊1包含

MySQL 深入理解索引B+樹儲存 (二)

摘要 本文以MySQL資料庫為研究物件,討論與資料庫索引相關的一些話題。特別需要說明的是,MySQL支援諸多儲存引擎,而各種儲存引擎對索引的支援也各不相同,因此MySQL資料庫支援多種索引型別,如BTree索引,雜湊索引,全文索引等等。為了避免混亂,本文將只關注於BTree索引,因為這是平常使用

深入理解四種資料庫索引型別(- 唯一索引/非唯一索引

唯一索引/非唯一索引主鍵索引(主索引)聚集索引/非聚集索引組合索引唯一索引/非唯一索引唯一索引1.唯一索引是在表上一個或者多個欄位組合建立的索引,這個或者這些欄位的值組合起來在表中不可以重複。非唯一索引2.非唯一索引是在表上一個或者多個欄位組合建立的索引,這個或者這些欄位的值組合起來在表中可以重複,不要求唯一