更新於 2023/04/23閱讀時間約 12 分鐘

【資料庫寶典】 MongoDB - 進階索引

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});
  • 建立一筆Document時間為now。
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
  • 刪除重建索引為1秒鐘後失效。
db.test.dropIndex("created_at_1");
db.test.createIndex({created_at: 1}, {expireAfterSeconds: 1});
  • 再建立一筆Document時間為now。
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清除動作?

  • 預設60秒執行一次清除動作。
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()
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.