磁碟 I/O 效能是影響 MongoDB 資料函式庫整體效率的關鍵因素,尤其在資料量龐大或操作頻繁的環境下。本文旨在提供 MongoDB 磁碟 I/O 調校的最佳實務,涵蓋暫存檔案管理、日誌檔案最佳化、WiredTiger 儲存引擎組態以及資料檔案 I/O 最佳化等導向。此外,文章也將探討如何在複製集和 MongoDB Atlas 環境下進行效能調校,並提供實用的程式碼範例和圖表說明。藉由理解和應用這些技巧,開發者可以有效提升 MongoDB 資料函式庫的效能和穩定性。

磁碟I/O效能調校

在MongoDB的運作中,磁碟I/O是一個至關重要的效能因素。當資料量龐大或操作頻繁時,磁碟I/O可能會成為效能瓶頸,影響整體系統的運作效率。本章節將探討MongoDB中的磁碟I/O相關議題,包括暫存檔案的I/O、Journal機制,以及如何最佳化這些方面的效能。

暫存檔案的I/O

當MongoDB執行某些操作,如大規模的聚合查詢或排序操作時,可能會使用暫存檔案。這些暫存檔案通常儲存在_tmp目錄下。如果這些操作變得極端,可能會干擾到資料檔案和Journal的I/O,從而導致整體效能下降。

最佳化暫存檔案的I/O

  1. 增加internalQueryMaxBlockingSortMemoryUsageBytes引數:透過增加這個引數,可以允許更多的記憶體用於排序操作,從而避免使用暫存檔案。

  2. _tmp目錄放在高效能的儲存裝置上:考慮將_tmp目錄放在專用的SSD或雲端臨時磁碟上,以提高I/O效能。

Journal機制

MongoDB使用Journal機制來確保資料的一致性和永續性。當資料在WiredTiger快取中被修改時,這些修改首先被寫入Journal檔案。Journal檔案採用預寫日誌(Write-Ahead Log, WAL)的模式,這種模式可以順序寫入資料,從而提高寫入效能。

Journal相關統計資訊

透過db.serverStatus().wiredTiger.log可以取得Journal相關的統計資訊,包括:

  • log bytes written:寫入Journal的資料量。
  • log sync operations:Journal同步操作的次數。
  • log sync time duration (μsecs):同步操作所花費的時間。

這些統計資訊可以幫助我們瞭解Journal的寫入速率和同步延遲。

計算平均Journal同步時間

var journalStats = db.serverStatus().wiredTiger.log;
var avgSyncTimeMs = journalStats['log sync time duration (usecs)'] / 1000 / journalStats['log sync operations'];
print('Journal avg sync time (ms)', avgSyncTimeMs);

平均Journal同步時間是衡量Journal磁碟爭用的重要指標。一般來說,這個時間越短越好,但具體的最佳值取決於工作負載的特性。

將Journal放在專用裝置上

由於Journal的I/O特性與其他資料檔案不同,且資料函式庫修改通常需要等待Journal寫入完成,因此在某些情況下,將Journal放在專用的高速裝置上可以提高效能。

如何移動Journal到專用裝置

  1. 掛載新的外部磁碟裝置
  2. 將Journal檔案移動到新的裝置上

這樣可以提高Journal的寫入效能,從而改善整體的資料函式庫效能。

MongoDB 磁碟 I/O 最佳化

日誌檔案最佳化

將日誌檔案(journal)移到獨立的裝置上,可以顯著提高寫入效能。以下是一個範例,展示如何將日誌檔案移到 /dev/sde 裝置:

$ cd /var/lib/mongodb
$ service mongod stop
$ mv journal OldJournal
$ mkdir journal
$ mount /dev/sde journal
$ cp -p OldJournal/* journal
$ chown -R mongod:mongod journal
$ chcon -R -u system_u -t mongod_var_lib_t journal
$ service mongod start

內容解密:

  1. 停止 MongoDB 服務service mongod stop 用於停止 MongoDB 服務,以確保在移動日誌檔案時不會有任何寫入操作。
  2. 掛載新裝置:將 /dev/sde 裝置掛載到 journal 目錄,並將原有的日誌檔案複製到新裝置。
  3. 設定許可權:使用 chownchcon 設定 journal 目錄的許可權和 SELinux 上下文,以確保 MongoDB 可以正確存取日誌檔案。
  4. 啟動 MongoDB 服務service mongod start 用於重新啟動 MongoDB 服務。

將日誌檔案移到獨立的裝置上後,需要確保該裝置在系統重新啟動後仍能正確掛載,方法是將相關設定新增到 /etc/fstab 檔案中。

資料檔案 I/O

對於大多數資料函式庫而言,讀取操作遠多於寫入操作。即使在更新密集的系統中,資料也需要在寫入前被讀取。只有在工作負載幾乎完全由批次插入組成時,寫入效能才會成為主導因素。

快取與磁碟讀取

WiredTiger 快取在避免磁碟讀取方面發揮著重要作用。如果檔案可以在快取中找到,則不需要從磁碟讀取。對於典型的負載,超過 90% 的檔案讀取可以從快取中滿足。

相關統計資料:
  • application threads page read from disk to cache count:記錄從磁碟讀取到快取的頁面數量。
  • application threads page read from disk to cache time (usecs):記錄從磁碟讀取到快取所花費的時間(微秒)。
var cache = db.serverStatus().wiredTiger.cache;
var reads = cache['application threads page read from disk to cache count'];
var time = cache['application threads page read from disk to cache time (usecs)'];
print('avg disk read time (ms):', time / 1000 / reads);

內容解密:

  1. 計算平均磁碟讀取時間:透過 db.serverStatus() 取得相關統計資料,計算平均磁碟讀取時間。
  2. 評估 I/O 子系統健康狀況:平均讀取時間是評估 I/O 子系統健康狀況的重要指標。一般而言,平均讀取時間應小於 10ms,即使用磁碟;如果使用 SSD,則應低於 1ms。

資料檔案寫入

WiredTiger 非同步寫入資料檔案,大多數情況下,應用程式不需要等待這些寫入完成。然而,當寫入 I/O 成為瓶頸時,驅逐(eviction)過程將阻塞操作,直到快取中的髒資料(dirty data)被足夠清除。

相關統計資料:

  • application threads page write from cache to disk count:記錄從快取寫入到磁碟的頁面數量。
  • application threads page write from cache to disk time (usecs):記錄從快取寫入到磁碟所花費的時間(微秒)。

跨多個裝置分割資料檔案

通常的做法是將所有資料檔案放在單一檔案系統上,由組態為 RAID 10 的磁碟陣列支援。然而,在某些情況下,將特定的資料元素對映到獨立裝置上可能是值得的。

例如,可以將「冷」歸檔資料存放在廉價的磁碟上,而將「熱」資料存放在高效能的 SSD 上。

組態範例:

storage:
  dbPath: /mnt/mongodb/mongoData/rs1
  directoryPerDB: true
journal:
  enabled: true

內容解密:

  1. directoryPerDB: true:將每個資料函式庫的資料檔案存放在獨立的目錄中。
  2. directoryForIndexes:雖然未在此範例中展示,但可以設定為 true,以將索引和集合檔案存放在獨立的子目錄中。

這種組態使得跨多個裝置分割資料檔案變得更容易,特別是在初始建立資料函式庫時進行規劃。

MongoDB 磁碟 IO 最佳化

WiredTiger 儲存引擎組態

MongoDB 使用 WiredTiger 儲存引擎,其組態對於磁碟 IO 效能有直接影響。以下是一個範例組態:

wiredTiger:
  engineConfig:
    cacheSizeGB: 16
    directoryForIndexes: true

內容解密:

  1. cacheSizeGB: 設定 WiredTiger 快取大小為 16GB。適當的快取大小可以減少磁碟 IO 需求。
  2. directoryForIndexes: 將索引檔案存放在獨立的目錄中,有助於 IO 分散和最佳化。

資料儲存結構

dbPath 目錄下,MongoDB 的資料儲存結構如下所示:

├── _tmp
├── admin
│ ├── collection
│ │ ├── 13--419801202851022452.wt
│ │ ├── 21--419801202851022452.wt
│ │ └── 23--419801202851022452.wt
│ └── index
│ ├── 14--419801202851022452.wt
│ ├── 22--419801202851022452.wt
│ ├── 24--419801202851022452.wt
│ └── 25--419801202851022452.wt
├── config
│ ├── collection
│ │ ├── 17--419801202851022452.wt
│ │ ├── 19--419801202851022452.wt
│ │ └── 34--419801202851022452.wt
│ └── index
│ ├── 18--419801202851022452.wt
│ ├── 20--419801202851022452.wt
│ ├── 35--419801202851022452.wt
│ └── 36--419801202851022452.wt
├── diagnostic.data
│ └── metrics.2020-10-04T07-12-03Z-00000
├── journal
│ ├── WiredTigerLog.0000000014
│ ├── WiredTigerPreplog.0000000014
│ └── WiredTigerPreplog.0000000015
├── sizeStorer.wt
└── storage.bson

內容解密:

  1. 資料函式庫目錄結構:每個資料函式庫都有獨立的目錄,包含 collection 和 index 子目錄。
  2. collectionindex 目錄:分別存放資料檔案和索引檔案,採用 WiredTiger 檔案格式(.wt)。
  3. journal 目錄:存放日誌檔案,用於保證資料的一致性和永續性。

IO 瓶頸檢測

要檢測 IO 是否過載,可以觀察以下指標:

  1. 平均讀取時間:從磁碟讀取資料到快取的平均時間不應超過 1-2ms(針對 SSD)。
  2. 作業系統統計資訊:使用 iostat(Linux)或 Get-Counter(Windows)等工具檢查磁碟佇列長度和等待時間。

Linux 下使用 iostat

iostat -xm -o JSON sdc 5 2 | jq

輸出範例:

{
  "avg-cpu": {
    "user": 45.97,
    "nice": 0,
    "system": 3.63,
    "iowait": 1.81,
    "steal": 0,
    "idle": 48.59
  },
  "disk": [
    {
      "disk_device": "sdc",
      "r/s": 0.4,
      "w/s": 49.2,
      "rkB/s": 15.2,
      "wkB/s": 2972,
      "rrqm/s": 0,
      "wrqm/s": 0.4,
      "rrqm": 0,
      "wrqm": 0.81,
      "r_await": 15.5,
      "w_await": 42.55,
      "aqu-sz": 2.08,
      "rareq-sz": 38,
      "wareq-sz": 60.41,
      "svctm": 0.87,
      "util": 4.32
    }
  ]
}

內容解密:

  1. aqu-sz(佇列大小):較高的值表示磁碟佇列較長,可能意味著磁碟過載。
  2. r_await(平均讀取等待時間):超過 10ms 可能表示磁碟效能不足或組態不當。

處理 IO 瓶頸

面對 IO 瓶頸,有兩種主要解決方案:

  1. 降低 IO 子系統的需求:最佳化查詢、索引和組態 WiredTiger 快取。
  2. 提升 IO 子系統頻寬:根據硬體平台,採用更快速的磁碟或增加磁碟。

增加 IO 子系統頻寬的方法

  1. 更換為更快速的磁碟:例如,使用 SLC SSD 取代 MLC SSD 或傳統硬碟。
  2. 使用 NVMe/PCIe SSD:相較於 SATA/SAS SSD,具有更低的延遲和更高的效能。
  3. 增加磁碟並分散資料:在有多餘插槽的伺服器上,可以新增磁碟並將資料分散儲存。

提升 MongoDB 效能:複製集與 Atlas 的最佳實踐

在前面的章節中,我們探討了單一 MongoDB 伺服器的效能調校。然而,大多數生產環境中的 MongoDB 例項都是以複製集(Replica Sets)的形式組態,以提供現代「永遠線上」應用程式所需的高用性保證。

複製集基礎

複製集遵循最佳實踐,由一個主要節點(Primary Node)和兩個或多個次要節點(Secondary Nodes)組成。建議使用三個或更多節點,且總節點數為奇數。主要節點接受所有寫入請求,並將其同步或非同步傳播到次要節點。當主要節點故障時,會發生選舉,選出一個次要節點作為新的主要節點,以繼續資料函式庫操作。

複製集的效能影響

在預設組態下,複製集的效能影響最小。所有讀寫操作都將導向主要節點,而主要節點在傳輸資料到次要節點時會產生一些額外負擔,但這種開銷很少是關鍵性的。

儲存陣列最佳化

如果您的 IO 服務由儲存陣列提供,並且遇到 IO 瓶頸,那麼您應該檢查以下幾點:

  • 陣列內的裝置型別:一些儲存陣列混合使用磁碟和 SSD 以降低儲存成本。然而,這種混合陣列可能提供不可預測的效能,尤其是對於資料函式庫工作負載。如果可能,您的儲存陣列應該只包含高速 SSD。
  • 陣列中的裝置數量:陣列的最大 IO 頻寬取決於陣列中的裝置數量。大多數陣列允許在不停機的情況下新增裝置,這可能是增加陣列 IO 容量的最簡單方法。
  • 陣列中使用的 RAID 等級:對於資料函式庫工作負載,RAID 10(「Stripe And Mirror Everything」)幾乎總是正確的 RAID 等級,而 RAID 5 或 6 幾乎總是錯誤的選擇。如果供應商試圖告訴您,他們的 RAID 5 具有某種神奇的技術,可以避免 RAID 5 寫入懲罰,那麼對於資料函式庫工作負載來說,RAID 5 幾乎總是壞訊息。

雲端儲存最佳化

如果您的伺服器執行在 AWS、Azure 或 GCP 等雲端環境中,那麼增加 IO 頻寬的常見方法是重新組態虛擬磁碟。您可以輕鬆更改附加磁碟的型別和組態的 IOPS。在某些情況下,需要重新啟動虛擬機器才能實施更改。

MongoDB Atlas 最佳化

對於根據 Atlas 的伺服器,更改 IO 等級更加簡單。Atlas 控制檯允許您選擇所需的 IOPS 等級。無需重新啟動伺服器,但會發生一系列主要節點降級,以將更改遷移到複製集。

重點提示

  • 為了讓依賴儲存陣列 IO 的資料函式庫伺服器獲得最佳效能,請確保使用的裝置是高速 SSD,裝置數量足以滿足 IO 需求,並且 RAID 組態是 RAID 10,而不是 RAID 5 或 RAID 6。
  • 在 AWS、Azure、GCP 或 Atlas 上更改雲端 MongoDB 伺服器的 IO 等級,可以透過幾次點選完成,有時無需停機。

圖表說明

圖 12-10:更改 AWS 磁碟區的 IOPS

此圖示展示瞭如何在 AWS 中調整 EBS 磁碟區的最大 IOPS。

圖 12-11:調整 Atlas 伺服器的 IO

此圖示展示瞭如何在 MongoDB Atlas 中調整 IO 等級。

程式碼說明

// 調整 MongoDB Atlas 中的 IO 等級範例
const adjustIO = async () => {
    // 連線到 MongoDB Atlas
    const client = new MongoClient(process.env.ATLAS_URI);
    try {
        await client.connect();
        // 調整 IO 等級
        const adminDb = client.db('admin');
        const result = await adminDb.command({
            setClusterParameter: {
                diskSizeGB: 100,
                // 其他引數...
            }
        });
        console.log(result);
    } catch (err) {
        console.error(err);
    } finally {
        await client.close();
    }
};

adjustIO();

程式碼解密:

  1. 連線到 MongoDB Atlas:使用 MongoClient 連線到 MongoDB Atlas。
  2. 調整 IO 等級:透過執行 setClusterParameter 命令來調整 IO 等級,例如更改 diskSizeGB
  3. 處理結果:輸出命令執行結果或錯誤訊息。
  4. 關閉客戶端:完成操作後關閉 MongoClient 連線。

Replica Sets 與 Atlas 的效能挑戰與機會

  • 額外效能挑戰:在 Replica Sets 中,主要節點和次要節點之間的資料同步可能會帶來額外的效能挑戰。
  • 效能機會:合理組態 Replica Sets 和使用 MongoDB Atlas 可以提供額外的效能機會,例如提高用性和可擴充套件性。