武林內功,資料庫的索引

weixin_34253539發表於2018-11-06

想把Mongodb真正的使用好,不是那麼簡單,不能只會增刪改查,還需要練習內功。
內功在武俠小說裡面是一個人發展強大起來的重要基礎,在我們Mongodb中練習內功也有這樣的作用。
開始今天的內功學習

為什麼需要索引

索引:提高查詢效率最有效的手段。是解決查詢速度緩慢而退出的一種特殊的資料結構,以易於遍歷的形式儲存部分資料內容;索引資料儲存在記憶體當中,同樣加快了索引查詢資料的效率。

從索引的簡介中瞭解兩個個知識點:

 ●  目的提高查詢速度。
 ●  索引儲存在記憶體當中。

索引針對的是查詢速度緩慢資料量大特別是資料量在百萬級別,千萬級別以及以上的資料量。
索引能大大減少查詢時間的損耗。

eg:自己寫過一段Monodb中的關聯查詢,資料表資料在百萬級別,沒有使用索引的時刻查詢時間在7s,使用索引後查詢時間是0.3s。效率大大提高。

Mongodb的索引機制

在往Mongodb中插入文件,每個文件都會經過底層的儲存引擎持久化操作之後,會展示一個位置資訊。
通過這個位置 資訊,就能從儲存引擎中讀取到資料。不同的儲存引擎處處位置的資訊不同。選擇合適的引擎也能幫助我們快速的查詢資料。
eg: wiredtiger引擎生成一個KEY值,通過KEY去訪問對應的文件。mmapv1引擎裡面位置資訊是通過檔案id與檔案內的偏移量決定的。

索引的型別

在Mongodb中有很多種索引支援,包含以下索引型別:單欄位索引,聯合索引,多key索引,文字索引, 地理位置索引,雜湊索引.不同的索引型別支援不同型別的資料格式和查詢需求。

單欄位索引

單欄位索引是針對單個欄位進行設定索引的操作。

//建立索引的語法
db.getCollection('test').createIndex({name:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1.0
}
數字1 是索引裡面的資料按照升序進行排序,需要按照降序排序的索引可以寫-1
db.getCollection('test').createIndex({name:-1})

程式碼中針對name欄位進行了建立索引,特別是Mongodb的主鍵_Id索引也是單欄位索引。

聯合索引

聯合索引在單欄位索引上進行了多個欄位操作,將多個欄位合併為一個索引的聯合索引。

//建立索引的語法還是一樣的。
db.getCollection('test').createIndex({name:1,phone:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1.0
}

在查詢欄位中引入聯合索引,在查詢語句操作時需要按照聯合索引的順序進行查詢,否則不能走索引的操作。
eg:我們建立索引時name在前 phone在後。

//find操作
db.getCollection('test').find({name:"qiiq"})
db.getCollection('test').find({name:"qiiq",phone:12512135})
這兩種操作是能走聯合索引。
//下面兩種操作時不能走聯合索引
db.getCollection('test').find({phone:12512135,name:"qiiq"})
db.getCollection('test').find({phone:12512135})

多key索引

多key索引:當內容是陣列或者list集合建立的一種索引。該索引會為陣列中的每個欄位建立索引。

子文件索引

該索引用來嵌入子文件中的欄位進行建立索引。操作也可以有複合索引,單欄位索引。

db.getCollection('test').createIndex({"user.name":1})

索引的屬性

在Mongodb中不僅支援多個型別的索引,還能對索引增加一些額外的屬性。

 ●  唯一索引:在Mongodb中_id就是利用單欄位索引加唯一索引的屬性,構成的。
 ●  部分索引(3.2版本之後新增):僅索引符合指定過濾器表示式集合中的文件。部分索引有較低的儲存要求,降低索引的建立與維護。
 ●  稀疏索引: 確保索引僅包含具有索引欄位的文件的條目。會跳過沒有索引欄位的文件。

 ●  TTL索引:在一定時間後自動從集合中刪除文件的一種索引。

索引的操作

索引的操作包含 建立,檢視 ,刪除,重建操作。

索引的建立

我們在前面的操作操作中已經使用索引的建立

db.getCollection('test').createIndex({"user.name":1})
db.collection.createIndex(keys,選項)
  1. keys,要建立索引的引數列表。如:{KEY:1},其中key表示欄位名,1表示升序排序,也可使用使用數字-1降序。

  2. options,可選引數,表示建立索引的設定。可選值如下:

 ●  background,Boolean,在後臺建立索引,以便建立索引時不阻止其他資料庫活動。預設值 false。
 ●  unique,Boolean,建立唯一索引。預設值 false。
 ●  name,String,指定索引的名稱。如果未指定,MongoDB會生成一個索引欄位的名稱和排序順序串聯。
 ●  dropDups,Boolean,建立唯一索引時,如果出現重複刪除後續出現的相同索引,只保留第一個。
 ●  sparse,Boolean,對文件中不存在的欄位資料不啟用索引。預設值是 false。
 ●  v,index version,索引的版本號。

 ●  weights,document,索引權重值,數值在 1 到 99,999 之間,表示該索引相對於其他索引欄位的得分權重。

檢視索引

getIndexes()檢視集合的所有索引。

db.getCollection('test').getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.test"
},
{
"v" : 2,
"key" : {
"name" : 1.0
},
"name" : "name_1",
"ns" : "test.test"
},
{
"v" : 2,
"key" : {
"name" : 1.0,
"phone" : 1.0
},
"name" : "name_1_phone_1",
"ns" : "test.test"
}
]

totalIndexSize()檢視集合索引的總大小。

db.getCollection('test').totalIndexSize()
69632 //單位位元組

索引的優化

慢查詢檢視

在mysql資料庫中,有慢查詢語句的展示,在Mongodb中也有這樣的實現名字是Profiling。
更改Mongodb的閾值,有三個級別的性質。

 ●  0 代表的是不開啟慢分析性質。
 ●  1 根據處理時間將超過閾值的請求記錄都記錄到system.profile集合中。
 ●  2 所有記錄都將記錄到集合system.profile中。
在隨著業務的發展,剛開始建立的索引可能不符合現在的業務需求。索引的數量並不是越多越好。
索引能幫助我們提高查詢的效能,但是會影響到插入和更新的效能。寫入與更新操作每次都需要把索引更新。

在此就可以根據慢請求的日誌,進行索引建立的調整。

索引分析

Mongodb中有一個命令explain();幫助我們進行查詢的慢分析。

db.getCollection("test").find().explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.test",
"indexFilterSet" : false,
"parsedQuery" : {},
"winningPlan" : {
"stage" : "COLLSCAN", //代表的是進行的全盤掃描,沒有利用到索引。當然也是查詢條件中沒有指定條件語句所致
"direction" : "forward"
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "237ae74dd4d9",
"port" : 27017,
"version" : "4.0.3",
"gitVersion" : "7ea530946fa7880364d88c8d8b6026bbc9ffa48c"
},
"ok" : 1.0
}

在name欄位增加索引,執行查詢計劃。

db.getCollection("test").find({"name":"frq"}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.test",
"indexFilterSet" : false,
"parsedQuery" : {
"name" : {
"$eq" : "frq"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1.0,
"phone" : 1.0
},
"indexName" : "name_1_phone_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : [],
"phone" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"frq\", \"frq\"]"
],
"phone" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "FETCH", 執行完索引後,進行FETCH,讀取出最終的
"inputStage" : {
"stage" : "IXSCAN", // 重點是這裡 用到了索引欄位,先在索引中查詢。
"keyPattern" : {
"name" : 1.0
},
"indexName" : "name_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"frq\", \"frq\"]"
]
}
}
}
]
},
"serverInfo" : {
"host" : "237ae74dd4d9",
"port" : 27017,
"version" : "4.0.3",
"gitVersion" : "7ea530946fa7880364d88c8d8b6026bbc9ffa48c"
},
"ok" : 1.0
}



原文釋出時間為:2018-11-5

本文作者:琪琪

本文來自雲棲社群合作伙伴“LuckQI”,瞭解相關資訊可以關注“LuckQI”。

相關文章