02.14 工業領域基於MongoDB設計時序數據庫及應用

首先什麼是時序數據庫

工業領域大部分都是使用的實時數據庫,對於物聯網到來的今天,傳統的實時數據庫已經不能滿足現在的需求。他們有很多共同點:

  • 都帶有時間戳
  • 按時間順序生成的
  • 大多為結構化數據
  • 採集頻率高
  • 數據量大

實時數據庫一般具備一下特定:

高速寫入的能力:工業實時數據庫通常會對寫入的速度有很高的要求。以流程工業的場景為例,每個環節都會設置傳感器,每個傳感器的採集頻率都很高,所以寫入的併發量會特別大,有時甚至會要求每秒上百萬的測點。所以除了對軟件的要求之外,也會選用一些高性能的服務器。

快速查詢的能力:查詢的需求分為兩塊,一是要響應實時的查詢請求,用於及時反映系統的狀態;二是歷史數據也要能快速被查詢,由於歷史數據的量非常大,在查詢時需要對特定時間段的數據做聚合,需要做到即使是查一整年的數據情況,也能很快的反應出來。

超強數據壓縮能力:上面提到監控數據會被存儲很長時間,5年甚至是10年都是常有的事,在存儲容量有限的情況下,就需要對數據做一定的壓縮,通常壓縮方式會分成無損壓縮和有損壓縮,相比而言,有損壓縮的壓縮比會更大一些,有時甚至會達到1:30-40,這就需要設計合理的算法來保留數據中的細節,使數據在還原後仍能保留重要的特徵。

積累豐富的工具:傳統的實時數據庫的解決方案一般是從採集開始到直可視化的一整套系統,有多年積累形成的豐富的工具包,比如會積攢上百種的協議,或者各種場景的數據模型,這些都是工業軟件的重要競爭力。

追求極致穩定

:工業上對軟件的穩定性要求特別高,除了用主備來保證高可用外,完全由軟件的質量來保證程序的持續運行,工程師會自豪地拍胸脯保證軟件跑十年也不會出錯。

時序數據庫特點:

1. 單條數據不會很長,但是數據量很大

2. 它們都帶有時間戳,且按順序生成

3. 數據大部分都是結構化的,用於描述某個參數在某個時間點的特徵

4. 寫入的頻率會比查詢的頻率高很多

5. 已存儲的數據很少有更新的需求

6. 用戶會更關心一段時間的數據特徵,而不是某一個時間點

7. 數據的查詢分析大多基於某一個時間段或者某個數值範圍

8. 需要進行統計和可視化的展示

隨著IoT和工業互聯網帶來的新一波風口,一系列新的生產方式、組織方式和商業模式開始湧現。物聯網技術逐步滲透工業,不斷增長的傳感器、飆升的數據量,以及更高的大數據分析需求對實時數據庫傳統的技術架構提出了挑戰。有些問題是需要直面的:

擴展性遇到瓶頸。傳統的技術架構雖然能保證單機具備極高的性能,也可以通過增加機器使性能線性擴展,但是不能像分佈式系統那樣實現動態靈活的擴容和縮容,需要提前進行規劃。當業務升級需要系統擴容時,老架構的擴展性就很難滿足需求了。

無法和大數據生態對接。數據採集的最終目的是被理解和使用,大數據產業中對於海量數據的存儲分析已經有很成熟的方案,不論是hadoop還是spark的生態圈,都面臨著新老技術的對接。很多工業企業因為想使用新的大數據分析技術,不得不對現有的系統進行升級或是替換。

價格高昂。傳統的工業實時數據庫解決方案價格都十分昂貴,一般只有大型企業能忍痛接受。但是隨著新技術新理念的普及,更多的中小企業也意識到數據的重要性,但考慮到資金投入,會傾向於尋找價格更低廉的方案。

這時候來自互聯網大家庭的時序數據庫方案就展現出了一些先天優勢,比如:

分佈式架構的天然優勢:傳統的實時數據庫多是主備的部署架構,通常要求有較高配置的機器,來追求單機極致的性能;同時,在穩定性方面,會對運行軟件的穩定性做極高的要求,完全由高質量的代碼來保證運行的穩定;由於存儲容量有限,也會要求超高的數據壓縮比。但是時序數據庫的分佈式架構,使得系統能夠輕鬆的進行水平擴展,讓數據庫不再依賴昂貴的硬件和存儲設備,以集群天然的優勢來實現高可用,不會出現單點的瓶頸或故障,在普通的x86服務器甚至是虛擬機上都可以運行,大大降低了使用成本。

更靈活的數據模型:傳統的實時數據庫由於工業場景的特殊性,常使用的是單值模型,一個被監控的參數稱為一個測點,在寫入時會對每一個測點建一個模型,比如一個風機的溫度指標算一個測點,十個風機的十個指標就是100個測點,每個測點會附帶描述信息(名稱、精度、數據類型、開關量/模擬量等)查詢的時候就會針對每個測點去查詢數值。單值模型的寫入效率會很高。

接下來就是我自己基於MongoDB設計時序數據庫架構的思路:(沒有一個架構設計能適合所有的應用場景規範。無論哪種架構,都需要權衡利弊)

對每個數據Tag,建立一個Collection 以PK命名 如下圖:

工業領域基於MongoDB設計時序數據庫及應用


工業領域基於MongoDB設計時序數據庫及應用

數據機構設計:

工業領域基於MongoDB設計時序數據庫及應用


工業領域基於MongoDB設計時序數據庫及應用

工業領域基於MongoDB設計時序數據庫及應用


工業領域基於MongoDB設計時序數據庫及應用

_id:MongoDB Document pk

Count:Document 數據大小

Data:數據 s:時間戳(差值:可以有效減數據大小)v:數值

StartTimeStamp:起始時間

LastTimeStamp:結束時間

可以看到數據存儲在Data裡面 隨著數據量不斷擴大 Data 線性增長 可以對Data數據進行簡單優化:

對 s v 數據類型進行設計可有效減少磁盤空間的佔用 s 時間戳 利用差值存儲 4byte 即可 根據需要存儲的數據類型 v float類型 也是 4byte 滿足大部分應用場景 這樣 每個數據 僅佔8byte 磁盤空間 當然還可以進一步優化及壓縮 以後再說。

按照數據數量分組分段(Document)

分段數據具有顯著的優勢。基於時間的分段將整整一段時間的數據存儲到單個文檔中。在諸如 IoT 的基於時間的應用中,傳感器數據可以以不規則的間隔生成,並且一些傳感器可以提供比其他傳感器數據更多的數據。在這些場景中,基於時間的分段可能不是架構設計的最佳方法。另一種策略是基於大小的分組。

通過基於大小的分組,我們根據一定數量的發射傳感器事件或一整天(以先到者為準)圍繞一個文檔設計我們的模式。

要查看基於大小的存儲分區,請考慮存儲傳感器數據並將存儲區大小限制為每個文檔3600個事件或一天(以先到者為準)的方案。注意:3600限制是任意數字,可以根據需要進行更改,無需更改應用程序或模式遷移。

如下圖:

工業領域基於MongoDB設計時序數據庫及應用


工業領域基於MongoDB設計時序數據庫及應用

數據庫設計完畢,目前場景下可滿足數據的存儲需求。但 IoT場景下有大量的傳感器及信息要存儲對於吞吐量有很大的要求。經過測試MongoDB 寫入數據速度並不是很快 大致5000條/秒(沒仔細測過,不同硬件配置 數值也可能不同),對於工業場景寫入數據量也不算很大,這樣需要對時序數據庫增加緩存隊列。可以採用Kafka,這裡我們自己設計一套簡單的消息隊列讓其滿足萬級數據量的隊列存儲。

緩存隊列設計如下:

00010203

04050607PK0000-0000FFFFFFFF

FFFFFFFF 

0000-03FF

var0 meta0000-0400head

tailDATA0000-0408data.k

data.v0000-0418data.k

data.v data.k

data.v0001-03FFdata.k

data.vvar1meta0001-0400head

tailDATA0000-0408data.k

data.v data.k

data.v data.k

data.v0002-03FFdata.k

data.vvar2meta0002-0400head

tailDATA0000-0408data.k

data.v data.k

data.v data.k

data.v0003-03FFdata.k

data.v

var1023meta03FF-0400head

tailDATA03FF-0408data.k

data.v data.k

data.v data.k

data.v03FF-03FFdata.k

data.v

利用MomeryMappedFile既能保證隊列存儲在內存中,又能防止數據丟失問題產生。打開一個文件,映射到內存一塊區域。灰色區域用來存儲Tag的PK,來標識數據。每個數都有一定大小(可設置)的存儲空間, 其中黃色區域是數據的mate 用來存儲隊列head(隊列第一個的地址)tail(隊列最後一個的地址),綠色區域DATA用來存儲有效數據 根據之前設計的數據結構data.k 存儲時間戳 data.v 存儲數值。

隊列(FIFO)的基本原理是:

入隊時:把入隊數據寫到tail後面的地址 並更新meta中tail的地址。需要增加一個判斷是否到達內存空間的最後一個地址如果當前tail是最後一個地址,則將數據寫到第一個地址並把meta中tail更新到第一個地址。如果tail中的地址正好等於head中的地址,則表示數據空間已滿,實現FIFO時head需要更新到下一個地址。

出隊時:讀取當前head地址中的數據,並更新head地址到下一個地址。需要增值一個判斷head地址=tail地址 則隊列為空,不做任何操作。

以上是內存映射隊列的一個基本思路,還需要考慮鎖和併發問題。目前用netcore2.1實現 並已經在項目中應用。

這樣一個簡單的時序數據庫,就設計完成了。當然還有很多不足,還需經過大量的測試驗證。


分享到:


相關文章: