MongoDB 副本集的讀寫效能最佳化對於應用程式至關重要。合理組態讀取偏好能有效降低主節點負載,提升讀取吞吐量。藉由 maxStalenessSeconds 引數,開發者可以根據業務需求平衡資料一致性和讀取效能。此外,標籤集的運用能更精細地控制讀取請求的路由,實作讀取負載平衡和特定資料中心的讀取需求。寫入關注級別的選擇則需考量資料安全性和寫入效能之間的平衡。對於全文檢索,Atlas Search 提供了多種分析器,需根據實際場景選擇合適的分析器以提升搜尋效率。最後,當單一副本集無法滿足效能需求時,分片技術能有效分散資料和負載,實作水平擴充套件。

MongoDB 副本集讀寫效能最佳化

在 MongoDB 副本集中,讀寫效能的最佳化對於整體系統的效能至關重要。預設情況下,所有讀取操作都被導向至主節點(Primary),但我們可以透過設定讀取偏好(Read Preference)來將讀取請求導向至副本文點(Secondary),從而提高讀取效能。

使用讀取偏好

讀取偏好允許我們控制 MongoDB 驅動程式將讀取請求導向至哪個節點。主要有以下幾種讀取偏好設定:

  • primary:所有讀取操作都被導向至主節點,這是預設設定。
  • primaryPreferred:優先將讀取操作導向至主節點,如果主節點不可用,則導向至副本文點。
  • secondary:將讀取操作導向至副本文點。
  • secondaryPreferred:優先將讀取操作導向至副本文點,如果沒有可用的副本文點,則導向至主節點。
  • nearest:將讀取操作導向至網路延遲最低的節點,無論是主節點還是副本文點。

圖表說明:不同讀取偏好對讀取效能的影響

@startuml
note
  無法自動轉換的 Plantuml 圖表
  請手動檢查和調整
@enduml

此圖示展示了不同讀取偏好設定下,從不同位置發出的查詢所耗費的時間。可以看到,使用 nearest 讀取偏好總是能夠獲得最佳的讀取效能。

設定讀取偏好

讀取偏好可以在連線 MongoDB 時設定,也可以在特定的查詢陳述式中設定。

在連線 URI 中設定

mongodb://n1,n2,n3/?replicaSet=rs1&readPreference=secondary

在查詢陳述式中設定(以 Node.js 為例)

const client = await mongo.MongoClient.connect(myMongoDBURI);
const collection = client.db('MongoDBTuningBook').collection('customers');
const options = {'readPreference': mongo.ReadPreference.NEAREST};
await collection.find({}, options).forEach((customer) => {
    count++;
});

使用 maxStalenessSeconds 引數

maxStalenessSeconds 引數可以用於控制允許的資料延遲。當選擇副本文點時,MongoDB 驅動程式只會考慮那些資料時間戳在 maxStalenessSeconds 秒內的主節點資料。最小值為 90 秒。

例如,以下 URI 指定了優先選擇副本文點,但只會選擇那些資料時間戳在 5 分鐘(300 秒)內的主節點資料的副本文點:

mongodb://n1,n2,n3/?replicaSet=rs1&readPreference=secondary&maxStalenessSeconds=300

程式碼範例與內容解密

以下是一個使用 maxStalenessSeconds 的程式碼範例:

const uri = "mongodb://n1,n2,n3/?replicaSet=rs1&readPreference=secondary&maxStalenessSeconds=300";
const client = new MongoClient(uri);
await client.connect();
const database = client.db('myDatabase');
const collection = database.collection('myCollection');
const query = { /* your query */ };
const options = { /* your options */ };
const cursor = collection.find(query, options);
await cursor.forEach(doc => console.log(doc));
await client.close();

內容解密:

  1. maxStalenessSeconds 引數的作用:此引數確保了當使用副本文點讀取資料時,資料的最新程度不會太舊,從而避免了因讀取過時資料而導致的錯誤。
  2. readPreference 設定為 secondary:這表示優先從副本文點讀取資料,如果沒有可用的副本文點,才會從主節點讀取。
  3. MongoClient 連線字串的組態:正確組態連線字串中的 replicaSetreadPreferencemaxStalenessSeconds 引數,可以靈活控制 MongoDB 的讀取行為。

副本集與標籤集的最佳實踐

在 MongoDB 的副本集架構中,標籤集(Tag Sets)提供了一種靈活的方式來控制讀取偏好(Read Preference),將查詢請求導向特定的次要節點或節點群。這種機制不僅能最佳化讀取效能,還能根據不同節點的角色或地理位置進行工作負載的分佈。

組態標籤集

首先,我們需要為副本集中的各個節點設定標籤。假設我們的副本集包含三個節點,分別位於香港、東京和韓國,我們可以按以下方式組態:

mongo> conf = rs.conf();
mongo> conf.members.forEach((m)=>{print(m.host);});
mongors01.eastasia.cloudapp.azure.com:27017
mongors02.japaneast.cloudapp.azure.com:27017
mongors03.koreacentral.cloudapp.azure.com:27017

mongo> conf.members[0].tags={"location":"HongKong","role": "prod" };
mongo> conf.members[1].tags={"location":"Tokyo","role":"BI" };
mongo> conf.members[2].tags={"location":"Korea","role": "prod" };
mongo> rs.reconfig(conf);
{
  "ok": 1,
  ...
}

組態解說:

  1. rs.conf():取得當前副本集的組態。
  2. conf.members.forEach((m)=>{print(m.host);}):列印所有成員的主機位址。
  3. 為每個成員設定 tags 屬性,包含 locationrole 標籤,用於區分節點的位置和角色。
  4. rs.reconfig(conf):重新組態副本集,使標籤生效。

使用標籤集進行讀取操作

組態完成後,我們可以根據標籤將讀取請求導向特定的節點。例如,將查詢導向具有 prod 角色的節點:

db.customers.find({ Phone: 40367898 }).readPref('secondaryPreferred', [{ role: 'prod' }]);

讀取偏好解說:

  1. readPref('secondaryPreferred', [{ role: 'prod' }]):優先從具有 prod 角色的次要節點讀取資料。
  2. 這種方式適合將特定型別的查詢導向特定的節點,例如將分析型查詢導向專門的 BI 節點。

工作負載分佈

利用標籤集,我們還可以將不同集合的查詢分散到不同的節點,從而提升整體效能。例如,將三個集合的查詢分別導向香港、韓國和東京的節點:

db.getMongo().setReadPref('secondaryPreferred', [{ "location": "HongKong" }]);
db.iotData1.aggregate(pipeline, { allowDiskUse: true });

工作負載分佈解說:

  1. setReadPref:為當前資料函式庫連線設定讀取偏好,將查詢導向香港節點。
  2. db.iotData1.aggregate:執行聚合查詢,並允許使用磁碟空間處理大資料集。

寫入關注(Write Concern)

寫入關註定義了 MongoDB 在確認寫入操作完成前需要滿足的條件,包括寫入的節點數量、日誌寫入要求和超時設定。

寫入關注引數:

  • w: 控制需要多少個節點接收寫入操作後才算完成,可設為數字或 "majority"
  • j: 是否要求寫入操作必須寫入日誌(journal)後才算完成。
  • wtimeout: 設定寫入關注的超時時間。

日誌寫入與效能考量

j:false 時,寫入操作在被 mongod 接收後即視為完成;而 j:true 則要求寫入操作必須寫入預寫日誌(write-ahead journal)。雖然停用日誌寫入可能提升效能,但在某些情況下會增加資料丟失的風險。

寫入關注的 w 選項

w 選項決定了寫入操作需要傳播到多少個節點才算完成。預設值為 1,即只需主節點接收寫入。設定為 "majority" 可以確保大多數節點接收到寫入,從而提高資料安全性。

圖示解析:

圖 13-3 展示了 {w:2,j:true} 的寫入關注流程:

  1. 主節點接收寫入並寫入日誌。
  2. 透過複製將寫入操作傳播到次要節點。
  3. 次要節點將寫入操作寫入其日誌後,寫入操作才算完成。

MongoDB 寫入關注(Write Concern)對效能的影響

在 MongoDB 中,寫入關注(Write Concern)是確保資料寫入的安全性和可靠性的機制。寫入關注的級別越高,資料寫入的安全性越高,但效能也會受到影響。

寫入關注級別與效能

根據圖 13-4 的測試結果,當寫入 50,000 筆資料時,不同的寫入關注級別對效能的影響非常顯著。結果顯示,w:0 的寫入關注級別提供了最佳的效能,但也意味著資料寫入的可靠性最低。

內容解密:

  • w:0:表示不需要確認寫入是否成功,這種模式下效能最佳,但資料可能會遺失。
  • w:1w:2w:3:表示需要確認寫入是否成功,分別對應不同的節點數量,這些模式下效能會降低,但資料可靠性提高。

寫入關注與次要讀取(Secondary Reads)

雖然提高寫入關注級別會降低修改作業的效能,但對於讀取密集型的應用程式來說,可能會有正面的影響。如果寫入關注級別設定為將資料寫入叢集中的所有成員,那麼次要讀取(Secondary Reads)將始終傳回正確的資料。

內容解密:

  • 當寫入關注級別設定為 w:n(n 為叢集中的節點數量)時,次要讀取將傳回最新的資料。
  • 但如果叢集中有節點故障,寫入作業可能會失敗。

MongoDB Atlas 與其功能

MongoDB Atlas 是 MongoDB 提供的完全託管的資料函式庫服務。使用 Atlas,可以輕鬆建立和組態 MongoDB 複本集(Replica Sets)和分片叢集(Sharded Clusters),無需自行組態硬體或虛擬機器。

Atlas Search 功能

Atlas Search 是根據 Apache Lucene 的全文檢索功能,提供了比 MongoDB 內建的文字索引更強大的搜尋能力。Atlas Search 提供了多種分析器(Analyzer),可以根據不同的需求選擇適合的分析器。

Atlas Search 分析器型別

  • Standard Analyzer:將所有文字轉換為小寫,忽略標點符號,並正確處理特殊符號和縮寫。
  • Simple Analyzer:類別似 Standard Analyzer,但處理「詞彙」(word)的邏輯較簡單。
  • Whitespace Analyzer:僅根據空白字元分割文字,不進行其他處理。
  • Keyword Analyzer:將整個欄位視為一個單元,需要完全匹配才能傳回結果。
  • Language Analyzer:針對不同語言提供預設的分析器,能夠根據語言特性進行索引。

內容解密:

  • 選擇適合的分析器可以改善搜尋結果的相關性,但也可能影響查詢效能。
  • 不同的欄位可能需要不同的分析器,例如描述欄位可能適合使用 Language Analyzer,而屬性型別欄位則適合使用 Keyword Analyzer。

圖表分析

圖 13-5 比較了不同分析器對索引大小的影響。結果顯示,不同分析器對索引大小的影響差異很大。

內容解密:

  • 索引大小會影響搜尋效能和儲存空間的需求。
  • 選擇分析器時需要考慮資料特性和查詢需求。

綜上所述,MongoDB 的寫入關注級別和 Atlas Search 功能都對效能和資料可靠性有重要影響。選擇合適的寫入關注級別和 Atlas Search 分析器,可以在保證資料可靠性的同時,最佳化應用程式的效能。

Atlas Search 分析器效能比較與資料湖應用

在建立 Atlas Search 索引時,選擇適當的分析器(Analyzer)對於搜尋效能和結果準確性至關重要。不同的分析器會對索引大小和查詢時間產生不同的影響。

索引大小與分析器型別的關係

從圖 13-5 中可以看出,不同的分析器型別對索引大小的影響在較小的文字欄位中差異不大,但在較大的文字欄位中則會有顯著的不同。標準分析器(Standard Analyzer)、簡單分析器(Simple Analyzer)、空白分析器(Whitespace Analyzer)、關鍵字分析器(Keyword Analyzer)和語言分析器(Language Analyzer)在索引大小上的表現各不相同。

索引大小比較

分析器型別小欄位索引大小大欄位索引大小
標準分析器0.45952.97
簡單分析器0.40025.52
空白分析器0.36766.10
關鍵字分析器0.51196.10
語言分析器0.40535.29

查詢時間與分析器型別的關係

圖 13-6 展示了不同分析器型別對查詢時間的影響。關鍵字分析器的查詢時間最短,但其搜尋結果可能不如其他分析器準確。

查詢時間比較

分析器型別查詢時間(秒)
標準分析器2.13
簡單分析器2.50
空白分析器2.10
關鍵字分析器1.99
語言分析器2.11

分析器型別的選擇

雖然關鍵字分析器的查詢時間最短,但其搜尋結果可能不佳。標準分析器在查詢時間和結果準確性之間取得了良好的平衡,因此通常是個不錯的選擇。

Atlas Data Lake 的應用

Atlas Data Lake 是 MongoDB 提供的一種用於查詢 Amazon S3 儲存桶中資料的工具。它允許使用者使用 MongoDB 查詢語言來查詢外部資料。雖然 Data Lake 看起來像一個普通的 MongoDB 資料函式庫,但它有一些限制,例如缺乏索引。

資料湖的限制與最佳化

由於 Data Lake 缺乏索引,查詢可能會變慢。為了最佳化查詢效能,可以將資料分成多個檔案,並使用檔名來反映關鍵屬性值,從而限制查詢的檔案範圍。

// 定義 Data Lake 中的 customers 集合
customers: [
  {
    definition: '/customers/{customerId string}',
    store: 's3store'
  }
]

程式碼解析

上述程式碼定義了 Data Lake 中的 customers 集合,將資料儲存在不同的檔案中,每個檔案對應一個客戶,檔名包含客戶 ID。這種設計可以最佳化查詢效能,減少查詢所需的檔案數量。

使用 Plantuml 圖表展示資料湖架構

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 使用 Plantuml 圖表展示資料湖架構

rectangle "資料儲存" as node1
rectangle "查詢介面" as node2
rectangle "查詢結果" as node3

node1 --> node2
node2 --> node3

@enduml

此圖示展示了 Atlas Data Lake 的架構,資料儲存在 Amazon S3 中,透過 Data Lake 提供查詢介面,使用 MongoDB 查詢語言進行查詢,並將結果傳回給應用程式。

分片技術在MongoDB中的應用與效能最佳化

在前一章中,我們探討了最常見的MongoDB佈署組態:複製集。複製集對於需要高用性的現代應用程式至關重要,因為單一的MongoDB例項無法滿足這些需求。正如我們所見,複製集可以透過次要寫入來進行有限的讀取擴充套件。但是,對於大型應用程式,特別是寫入工作負載超過單個叢集能力的情況,則需要佈署分片叢集。

分片基礎

我們在第2章中介紹了分片技術。在分片資料函式庫叢集中,選定的集合會被分散到多個資料函式庫例項中。每個分割槽被稱為一個「分片」。這種分割槽是根據分片鍵值進行的。

雖然複製集旨在提供高用性,但分片技術旨在提供更大的可擴充套件性。當工作負載(尤其是寫入工作負載)超過伺服器的容量時,分片提供了一種將工作負載分散到多個節點的方法。

資料湖中的檔案結構最佳化

在Atlas資料湖中,檔案結構的設計對於查詢效能有著重要的影響。透過將資料分割成多個檔案,並根據查詢鍵值命名檔案,可以顯著提高查詢效能。例如,將客戶資料分割成多個JSON檔案,每個檔案名對應一個客戶ID(如/customers/1234.json),這樣在查詢特定客戶資料時,資料湖只需掃描相關的檔案,而不是整個目錄。

程式碼範例:定義資料湖中的集合

{
  "name": "customersNew",
  "source": {
    "bucket": "datalake02",
    "path": "/customers/"
  },
  "partition": {
    "fields": [
      {
        "name": "customerId",
        "path": "$.customerId"
      }
    ]
  },
  "storage": [
    {
      "store": "s3store"
    }
  ]
}

內容解密:

  1. 定義集合名稱customersNew是新集合的名稱。
  2. 來源設定:指定資料來源的儲存桶和路徑。
  3. 分割欄位:根據customerId欄位對資料進行分割。
  4. 儲存設定:指定使用的儲存型別為s3store

透過這種方式定義集合,可以實作根據客戶ID進行高效查詢。

分片對效能的影響

分片技術對於提高MongoDB的可擴充套件性和效能至關重要。正確的分片策略可以顯著提高寫入效能和查詢效能。然而,分片也帶來了一些挑戰,如跨分片查詢的複雜性和維護成本。