時間序列資料和MongoDB:第二部分 - 架構設計最佳實踐
之前的文章“ ofollow,noindex"> 時間序列資料和MongoDB:第一部分 - 簡介 ”中,介紹了時間序列資料的概念,然後介紹了一些常見問題,可用於幫助收集時間序列應用程式。這些問題的答案有助於指導支援大批量生產應用程式部署所需的架構和 MongoDB 資料庫配置。現在,我們將重點介紹兩種不同的模式設計如何影響讀取,寫入,更新和刪除操作下的記憶體和磁碟利用率。0
在分析結束時,您可能會發現應用程式的最佳模式設計可能正在利用模式設計的組合。按照我們下面列出的建議,您將有一個良好的起點,為您的應用程式開發設計最佳架構,並適當調整您的環境。
設計時間序列模式
讓我們首先說,沒有一個架構設計能適合所有的應用場景規範。無論哪種架構,都需要權衡利弊。理想情況下,您希望在記憶體和磁碟利用率之間實現最佳平衡,以獲得滿足應用程式要求的最佳讀寫效能,並使您能夠同時支援資料讀取和時間序列資料流分析。
在這篇博文中,我們將介紹各種架構設計配置。首先,每個資料樣本儲存一個文件,然後使用每個時間序列時間範圍的一個文件和每個固定大小的一個文件來儲存資料。每個文件儲存多個數據樣本稱為分組。這將在應用程式級別實現,並且不需要在 MongoDB 中專門配置任何內容。藉助MongoDB 靈活的資料模型,您可以優化資料,從而為應用程式的要求提供最佳效能和粒度。
這種靈活性還允許您的資料模型隨著時間的推移適應新的要求 - 例如從不屬於原始應用程式設計的新硬體感測器捕獲資料。這些新感測器提供的元資料和屬性與您在原始設計中使用的感測器不同。有了這些靈活性,您可能會認為 MongoDB 資料庫是無主之地,無論發生什麼事情,您都可以快速找到一個充滿無組織資料的資料庫。MongoDB通過 模式驗證 提供儘可能多的控制,允許您完全控制並強制執行諸如必填欄位和可接受值範圍之類的事情,僅舉幾例。
為了幫助說明架構設計和分組如何影響效能,請考慮我們要儲存和分析歷史股票價格資料的場景。我們的樣本股票價格生成器應用程式每秒為其跟蹤的給定數量的股票建立樣本資料。一秒是本例中每個股票程式碼收集的最小資料時間間隔。如果您想在自己的環境中生成樣本資料,可以在GitHub上使用 StockGen 工具。值得注意的是,儘管本文件中的樣本資料使用了股票程式碼作為示例,但您可以將這些相同的設計概念應用於任何時間序列場景,例如物聯網感測器的溫度和溼度讀數。
用於生成樣本資料的 StockGen 工具將生成相同的資料並將其儲存在兩個不同的集合中:StockDocPerSecond
和 StockDocPerMinute,每個集合包含以下模式:
場景一:
每個資料點一個文件

圖一:表示每秒一個文件粒度的示例文件
場景二:
每分鐘一個文件的基於時間的分段

圖2:表示一分鐘粒度的示例文件
請注意,欄位“p”包含一個子文件,其中包含每分鐘的值。
設計架構比較
讓我們根據 StockGen 工具生成的4週數據,比較和對比儲存大小和記憶體影響的資料庫指標。在評估資料庫效能時,衡量這些指標非常有用。
對資料儲存的影響
在我們的應用程式中,最小級別的時間粒度是秒。如方案1中所述,每秒儲存一個文件對於來自關係資料庫背景的人來說是最舒適的模型概念。這是因為我們每個資料點使用一個文件,這類似於表格模式中每個資料點的行。如圖3和圖4所示,該設計將產生每單位時間最大數量的文件和集合大小。

圖3:文件計數隨時間的變化,比較每秒與每分鐘架構設計

圖4:每種方案的資料大小和儲存大小之間的比較
圖4顯示了每個集合的兩種尺寸。系列中的第一個值是儲存在磁碟上的集合的大小,而第二個值是資料庫中資料的大小。這些數字不同,因為 MongoDB 的 WiredTiger 儲存引擎支援靜態資料壓縮。從邏輯上講,PerSecond 集合是605MB,但在磁碟上它佔用大約190 MB的儲存空間。
對記憶體利用率的影響
大量文件不僅會增加資料儲存消耗,還會增加索引大小。在每個集合上建立了一個索引,並覆蓋了符號和日期欄位。與將自己定位為時間序列資料庫的一些鍵值資料庫不同,MongoDB提供了 二級索引, 使您可以靈活地訪問資料並允許您優化應用程式的查詢效能。

圖5:PerSecond 和 PerMinute之間的索引大小(MB)比較
兩個集合中每個集合中定義的索引的大小如圖5所示。當索引和最近使用的文件適合由WiredTiger 快取分配的記憶體(我們稱之為“工作集”)時,提供 MongoDB 的最佳效能。在我們的例子中,我們在4周內僅生成了5只股票的資料。鑑於這個小測試用例,我們的資料已經為 PerSecond 場景生成了一個大小為103MB的索引。請記住,有一些優化,如 sary/%23term-prefix-compression" rel="nofollow,noindex" target="_blank">索引字首壓縮 這有助於減少索引的記憶體佔用。但是,即使使用這些優化,正確的模式設計對於防止失控的索引大小也很重要。鑑於增長的軌跡,應用程式需求的任何變化,例如在我們的示例場景中跟蹤超過5種股票或超過4周的價格,將對記憶體施加更大的壓力,並最終需要索引分頁到磁碟。發生這種情況時,表現會降低。要緩解這種情況,請考慮水平擴充套件。
水平縮放
隨著資料大小的增加,當達到 MongoDB 副本集中託管的主要 mongod 伺服器的物理限制時,最終可能會水平擴充套件。
通過 MongoDB Sharding 水平擴充套件,可以提高效能,因為索引和資料將分佈在多個MongoDB 節點上。查詢不再針對特定的主節點。相反,它們由稱為查詢路由器(mongos)的中間服務處理,該服務將查詢傳送到包含滿足查詢的資料的特定節點。這對應用程式完全透明 - MongoDB會處理所有路由。
場景三:
基於大小的分組
比較之前的場景時的關鍵點是,分段資料具有顯著的優勢。方案2中描述的基於時間的分段將整整一分鐘的資料儲存到單個文件中。在諸如 IoT 的基於時間的應用中,感測器資料可以以不規則的間隔生成,並且一些感測器可以提供比其他感測器資料更多的資料。在這些場景中,基於時間的分段可能不是架構設計的最佳方法。另一種策略是基於大小的分組。
通過基於大小的分組,我們根據一定數量的發射感測器事件或一整天(以先到者為準)圍繞一個文件設計我們的模式。
要檢視基於大小的儲存分割槽,請考慮儲存感測器資料並將儲存區大小限制為每個文件200個事件或一天(以先到者為準)的方案。注意:200限制是任意數字,可以根據需要進行更改,無需更改應用程式或模式遷移。

圖6:稀疏資料的基於大小的分段
圖6中顯示了一個基於大小的示例儲存桶。在此設計中,嘗試將每個文件的插入限制為任意數量或特定時間段似乎很困難; 但是,使用 upsert 很容易,如下面的程式碼示例所示:

圖7:要新增到基於大小的儲存桶的示例程式碼
當新的感測器資料進入時,它只是附加到文件,直到樣本數達到200,然後由於我們的upsert:true子句而建立一個新文件。
此方案中的最佳索引將在 {deviceid:1,sensorid:1,day:1,nsamples:1} 上。當我們更新資料時,這一天完全匹配,這是超級高效的。查詢時,我們可以在單個欄位上指定日期或日期範圍,這也是有效的,並且使用 UNIX 時間戳首先和最後一個進行過濾。請注意,我們使用整數值。這些實際上儲存為 UNIX 時間戳,僅佔用32位儲存,而 ISODate佔用64位。雖然與 ISODate 相比沒有顯著的查詢效能差異,但如果您計劃最終獲得數 TB的攝取資料並且不需要儲存小於一秒的粒度,則儲存為UNIX時間戳可能會很重要。
固定大小的分段資料將產生非常類似的資料庫儲存和索引改進,如在場景2中每次分段時所見。這是在 MongoDB 中儲存稀疏的 IoT 資料的最有效方法之一。
如何處理舊資料
我們應該永久儲存所有資料嗎?超過特定時間的資料對您的組織有用嗎?舊資料應該如何訪問?它是否可以在您需要時從備份中簡單地恢復,還是需要線上並且可以作為歷史分析的活動存檔實時訪問使用者?正如我們在本系列博文的第1部分中所述,這些是在上線之前應該提出的一些問題。
處理舊資料有多種方法,根據您的具體要求,某些方法可能比其他方法更適用。選擇最符合您要求的產品。
預聚合
您的應用程式是否真的需要為多年前生成的每個事件提供單個數據點?在大多數情況下,保持這種資料粒度的資源成本超過了能夠隨時查詢到這個級別的好處。在大多數情況下,可以預先聚合和儲存資料以便快速查詢。在我們的股票示例中,我們可能只想將每天的收盤價儲存為值。在大多數體系結構中,預聚合值儲存在單獨的集合中,因為通常對歷史資料的查詢與實時查詢不同。通常使用歷史資料,查詢會查詢隨時間推移的趨勢與個別實時事件。通過將此資料儲存在不同的集合中,您可以通過建立更高效的索引來提高效能,而不是在實時資料之上建立更多索引。
離線檔案策略
歸檔資料時,與資料檢索相關的 SLA 是什麼?是否恢復可接受的資料備份,或者資料是否需要線上並準備好在任何給定時間查詢?這些問題的答案將有助於推動您的檔案設計。如果您不需要實時訪問歸檔資料,則可能需要考慮備份資料並將其從實時資料庫中刪除。可以使用 MongoDB Ops Manager 備份生產資料庫,或者如果使用 MongoDB Atlas 服務,則可以使用完全託管的備份解決方案。
使用 remove 語句刪除文件
通過資料庫備份或 ETL 過程將資料複製到歸檔儲存庫後,可以通過 remove 語句從MongoDB集合中 刪除資料 ,如下所示:

儘管TTL索引很方便,但請記住每分鐘都會進行一次檢查,並且無法配置間隔。如果您需要更多控制以便在一天的特定時間內不會發生刪除,則可能需要安排執行刪除的批處理作業,而不是使用TTL索引。
刪除集合刪除文件
請務必注意,使用 remove 命令或 TTL 索引會導致高磁碟I / O。 在可能處於高負載的資料庫上,這可能是不可取的。從實時資料庫中刪除記錄的最有效和最快捷的方法是刪除 集合 。如果您可以設計應用程式,使每個集合代表一段時間,當您需要存檔或刪除資料時,您需要做的就是刪除集合。這可能需要您的應用程式程式碼中的一些查詢才能知道應該刪除哪些集合。當您發出刪除時,MongoDB 也必須從所有受影響的索引中刪除資料,這可能需要一段時間,具體取決於資料和索引的大小。
線上檔案策略
如果仍需要實時訪問歸檔資料,請考慮這些查詢發生的頻率以及僅儲存預聚合結果是否足夠。
分片存檔資料
歸檔資料和保持資料實時可訪問的一種策略是使用 分割槽分片 來對資料進行分割槽。分片不僅有助於跨多個節點水平擴充套件資料,還可以標記分片範圍,以便將資料分割槽固定到特定分片。節省成本的措施可能是將存檔資料儲存在執行成本較低的磁碟的分片上,並定期調整分片本身定義的時間範圍。這些範圍將使平衡器自動在這些儲存層之間移動資料,為您提供分層的多維度儲存。
通過可查詢備份訪問存檔資料
如果不經常訪問您的歸檔資料並且查詢效能不需要滿足任何嚴格的延遲 SLA,請考慮使用 MongoDB Atlas 或 MongoDB OpsManager 的可查詢備份功能備份資料。 可查詢備份 允許您連線到備份並向備份本身發出只讀命令,而無需先恢復備份。
查詢資料池中的資料
MongoDB 是一種廉價的解決方案,不僅適用於長期存檔,也適用於您的資料池。投資Apache Spark 等技術的公司可以利用 MongoDB Spark Connector 。此聯結器將MongoDB 資料實現為 DataFrames 和 Datasets,以便與 Spark 和機器學習,圖形,資料流和 SQL API 一起使用。
關鍵要點
一旦應用程式在生產中生效並且大小為 TB 級,從資源的角度來看,任何重大變化都可能非常昂貴。考慮這樣一種情況,即您擁有6 TB的 IoT 感測器資料,並以每秒5萬次插入的速率累積新資料。讀取的效能開始成為一個問題,您意識到您沒有正確擴充套件資料庫。除非您願意停止應用,否則此配置中的架構更改(例如,從原始資料儲存遷移到分割槽儲存)可能需要構建填充程式,臨時暫存區域和各種臨時解決方案以將應用程式移動到新的架構。文章的寓意是規劃增長並正確設計適合您的應用程式的 SLA 和要求的最佳時間序列模式。
本文分析了兩種不同的模式設計,用於儲存股票價格的時間序列資料。最終贏得此股票價格資料庫的架構是否是您方案中最佳的架構?也許。由於時間序列資料的性質和典型的資料快速提取,答案實際上可能是利用針對讀取或寫入大量用例的集合的組合。好訊息是,使用 MongoDB 靈活的架構,很容易進行更改。實際上,您可以執行兩個不同版本的應用程式,將兩個不同的模式寫入同一個集合。但是,不要等到查詢效能開始受到影響才能找到最佳設計,因為將現有文件的 TB 遷移到新架構可能需要時間和資源,並延遲應用程式的未來版本。在進行最終設計之前,您應該進行實際測試。引用一句著名的諺語:“三思而後行”。
在下一篇部落格文章“ 使用 MongoDB 查詢,分析和呈現時間序列資料 ”中,我們將研究如何有效地從MongoDB 中儲存的時間序列資料中獲取價值。
主要提示:
-
不推薦使用 MMAPV1 儲存引擎,因此請使用預設的 WiredTiger 儲存引擎。請注意,如果您從幾年前開始閱讀較舊的架構設計最佳實踐,則它們通常基於較舊的 MMAPV1 技術構建。
-
瞭解時間序列應用程式的資料訪問要求。
-
架構設計會影響資源。關於模式設計和索引,“三思而後行”。
-
如果可能,使用真實資料和實際應用程式測試模式模式。
-
分段資料減少了索引大小,從而大大降低了硬體要求。
-
時間序列應用程式傳統上捕獲非常大量的資料,因此只建立它們對應用程式的查詢模式有用的索引。
-
考慮多個集合:一個集中於編寫大量插入和最近的資料查詢,另一個集合具有重疊在預聚合資料上的歷史查詢的分塊資料。
-
當索引的大小超過託管 MongoDB 的伺服器上的記憶體量時,請考慮水平擴充套件以擴充套件索引並載入多個伺服器。
-
確定資料到期的時間點以及要採取的操作,例如歸檔或刪除。
原文釋出時間為:2018-10-24
本文作者: Mongoing中文社群
本文來自雲棲社群合作伙伴“ Mongoing中文社群 ”,瞭解相關資訊可以關注“ Mongoing中文社群 ”。