MongoDB - 索引類型
本篇主要是介紹MongoDB有哪些進階的索引, 了解原理及功能後, 在應用上才能規劃出更有效率的索引, 而主要會談到以下五個索引類型:
複合索引。 部份索引。 多鍵索引。 全文索引。 TTL索引。
進入到索引類型之前先提醒一下,一個集合的索引數不能超過64個。
複合索引
限制: 最多只能31個欄位做一組複合索引。
建立方式:
db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )
// 補充: <type>就是1或-1,代表正序或倒序。
首先我們實際來建立一組索引,叫做「人員分數查詢」專用的索引。
db.collection.createIndex({ userid: 1, score: -1}, {name: "人員分數查詢專用索引"})
接著建立幾個樣本資料:
[
{
"userid": "aa1",
"score": 45
},
{
"userid": "ca2",
"score": 55
},
{
"userid": "ca2",
"score": 75
},
{
"userid": "ca2",
"score": 30
},
{
"userid": "nb1",
"score": 30
},
{
"userid": "xyz",
"score": 90
}
]
案例一: 查詢條件發揮複合索引效用。
接著我們來做個查詢分析,分析我們的條件是否在索引之下發揮最大效用。
db.getCollection('test').find({"userid": "ca2", "score": 30}).explain("executionStats")
我們可以發現到Mongo選出來的最佳查詢計畫stage是FETCH,這個我們可以理解為透過索引直接找到文本,而inputStage代表著我們先針對userid, score這兩個索引做掃描,關於查詢分析這邊先不探討,會另外開一篇專門介紹。
COLLSCAN:全表掃描。 IXSCAN:索引掃描。 FETCH: 根據索引到指定的document。
上述結果成功發揮索引效用。
案例二: 查詢條件未能發揮效用
假設今天只找分數不找人員
db.getCollection('test').find({"score": 30}).explain("executionStats")
我們可以發現到Mongo分析出來的查詢計畫是全局掃描,也就是未能發揮索引效用,因此索引建立的順序是有意義的,需要根據需求來進行索引設計。
適用場景: 查詢欄位不只一個,且複合索引的欄位符合建立索引的順序,才能發揮最大的效用*。*
部分索引
我們不想針對該欄位全部的value都進行索引,就可以使用這種索引方式,舉例來說,我們只想對age 25的值進行索引,建立方式如下:
db.persons.createIndex({country:1},{partialFilterExpression: {age: {$gt:25}}})
其實就是帶有過濾條件的索引,大數據的應用確實應該分析及規劃這個部分。 滿足查詢條件在查詢時才能發揮索引效用,假設我們條件改為age = 25,那麼Mongo的查詢計畫就會給出全局掃描的選項。
多鍵索引
其實就是對嵌套的文檔做索引。
全文索引
{
"id": "1",
"post_text": "enjoy the mongodb articles on Runoob",
}db.posts.ensureIndex({post_text:"text"})# 索引示意: 會以term做為索引的key來記載出現的位置...。
# enjoy => [1, 2...]
# the => [1...]
# ...
// 簡單的查詢mongodb這個詞。
db.posts.find( { $text: { $search: "mongodb" } } );
// 查詢mongodb或articles這兩個詞。
db.posts.find({$text:{$search:"mongodb articles"}});
// 查詢包含mongodb但不包含elasticsearch的文本, 會用`-`來表示不包含。
db.posts.find({$text:{$search:"mongodb -elasticsearch"}});
// 查詢「mongodb articles」這個片語的文本。
db.posts.find({$text:{$search:"\\"mongodb articles\\""}})
TTL索引
TTL索引常常用在log應用, 假設使用者操作的歷程我們只想要保留半年, 則我們可以針對Collection進行TTL索引的建立, 而Mongo背景服務就會定時幫我們清除過期的資料, 但是使用上有幾個特別要注意的地方如下:
清除的型態是什麼?
只能是BSON的Date型態才能被清除。 如果用整數的Timestamp會沒有作用。
如果Collection本身已經是Capped的話, 我們又設定TTL會發生什麼事情呢?
會發生以下的錯誤訊息, 由於我們基礎篇有提到Capped Collection如果要刪除Document只能對整個Collection進行刪除, 故我們如果需求上已經對某個Collection規劃為Capped時, 就不要在用TTL索引了!!!
2020-11-23T03:36:21.405+0000 E INDEX [TTLMonitor] Error processing ttl index: { v: 2, key: { creationTime: 1 }, name: "expiredIdx", ns: "qa.qm_event_log", expireAfterSeconds: 1, background: true } -- 10089 cannot remove from a capped collection
如何更新TTL失效時間?
已建立的索引, 如果動態更改TTL參數會發生錯誤如下。
db.createIndex(...)
Index with name: expiredIdx already exists with different options
使用DB Command「collMod」來更新。
// Update the index with 120 seconds expiration threshold
db.runCommand({
collMod: 'ttldemo',
index: {keyPattern: {created_at: 1}, expireAfterSeconds: 120}
});
刪除重建後,之前的Documents會套用嗎?
db.test.createIndex({created_at: 1}, {expireAfterSeconds: 600});
db.test.insert({
created_at: new Date()
})
此時我們先確保背景服務不會去刪除我們的Document
2020-11-23T04:22:21.437+0000 D INDEX [TTLMonitor] ns: qa.test key: { created_at: 1.0 } name: created_at_1
2020-11-23T04:22:21.437+0000 D INDEX [TTLMonitor] deleted: 0
db.test.dropIndex("created_at_1");
db.test.createIndex({created_at: 1}, {expireAfterSeconds: 1});
db.test.insert({
created_at: new Date()
})
此時已有兩筆, 我們來看看是否兩筆都會套用? 答案是會的!
2020-11-23T04:25:21.439+0000 D INDEX [TTLMonitor] ns: qa.test key: { created_at: 1.0 } name: created_at_1
2020-11-23T04:25:21.439+0000 D INDEX [TTLMonitor] deleted: 2
如何監控TTL是否執行?
預設的MongoDB不會將TTL訊息印出, 如果我們打算讓該Log訊息印出, 則可以透過以下的配置來完成。
db.setLogLevel(1, "index");
印出訊息如下, Log會告知我們哪個時間點清除了哪些資料:
2020-11-23T03:40:21.407+0000 D INDEX [TTLMonitor] ns: admin.logsfilter key: { expiredAt: 1 } name: expiredAt_1
2020-11-23T03:40:21.407+0000 D INDEX [TTLMonitor] deleted: 0
2020-11-23T03:40:21.407+0000 D INDEX [TTLMonitor] ns: qa.error_log key: { creationTime: 1 } name: expiredIdx
2020-11-23T03:40:21.407+0000 D INDEX [TTLMonitor] deleted: 0
MongoDB背景多久執行一次TTL清除動作?
db.adminCommand({getParameter:1, ttlMonitorSleepSecs: 1});{
"ttlMonitorSleepSecs" : 60,
"ok" : 1.0
}
如何更改執行間隔(盡量不要太短, TTL索引過多將造成CPU負荷)。
> db.adminCommand({setParameter:1, ttlMonitorSleepSecs: 3600}); // 1 minute
{ "was" : 60, "ok" : 1 }
TTL刪除了多少Document
db.serverStatus().metrics.ttl
{
"deletedDocuments" : NumberLong(13533),
"passes" : NumberLong(20374)
}
常用的指令
# 查看collection佔用索引的容量
db.collection.totalIndexSize()