MongoDB——漸進式開發光伏雲系統實踐(二)
萬物生長——漸進式開發光伏雲系統實踐(一)
MongoDB——漸進式開發光伏雲系統實踐(二)
資料庫選型
nn
傳統工業監控系統中,往往採用SQL型資料庫。
nn資料庫中存有「實時」與「歷史」兩類數據表,設備定時採集的數據首先送入實時表中,實時表只存放最近時刻數據,新數據入庫會覆蓋之前的數據。表中保持只有一個時刻的數據,以提高讀寫速度。歷史表通過專門的存儲過程,定時讀取實時表中的數據,並加上時間欄位作為一條歷史記錄。根據數據量的不同,歷史表可能會按年、月或日分割為多個表。
nn在初代光伏雲系統的開發中,我們採用SQL型資料庫,表的設計照搬傳統現場監控系統,但很快發現了問題:
- 數據入庫的頻率與歷史記錄的頻率一樣都是5分鐘,所以實時表中的記錄就是歷史表中最頂端的一條記錄,而且數據分析的功能往往需要的是一段時間的數據,所以實時表雖然在不停被修改,但功能幾乎用不到實時表。
- 歷史表按月按年的固定劃分不一定符合功能設計的需要,一些「靈活」的查詢功能實現起來很繁瑣。
光伏雲系統的設備原始數據被我們稱為「核數據」,以上的問題讓我們開始重新審視核數據的本質。
傳統監控系統部署在現場,主要功能為實時監控。光伏雲系統由於採用了基於HTTP協議的Web技術棧,且常常需要通過互聯網傳輸數據,並不適合承擔實時監控的任務,而主要實現分析、管理等功能。我們認為,核數據更類似於某種「定時log信息」:
- 核數據入庫之後,主要供查詢使用,幾乎不存在修改的需求。
- 核數據作為大數據分析的原料,往往在事後使用,且單個數據對結果影響較小,因此相對於現場系統的數據,核數據對實時性和完整性的要求並不高。
- 核數據因設備型號的不同,數據內容千差萬別,不會像現場系統擁有統一的固定的點表。
- 由於分布於各處的電站常常需通過GPRS上送數據,流量有限,採集間隔不能太密集,一般不低於5分鐘。
總之,核數據的特點就是注重非結構化的記錄與查詢,事務性要求不高,是不是和伺服器的log信息很像。
那麼資料庫的選型就順理成章了,首先事務性要求不高,關注大數據量的查詢,我們選擇NoSQL型資料庫而不是SQL型資料庫。在主流的NoSQL型資料庫中,我們不關注實時讀寫性能,所以排除Redis;我們不是部署在Hadoop上,且數據也不是列結構的,所以不用HBase;而log類型的數據,我們很自然的想到了基於文檔的MongoDB。
此外,MongoDB的操作語言為JavaScript,這也與我們系統的整體技術棧統一;文檔型存儲便於我們用反範式化的方式設計檔案數據集合,以檔案數據量的微弱增加換取查詢上的極大便利。
MongoDB中不再有表、欄位和記錄的概念,資料庫中按集合(Collection)組織數據,每條數據稱為一個文檔(Document)。對於核數據,我們採取每個實際設備對應一個集合的方式。相對於同型號的所有設備數據放在同一集合的方式,這樣單個集合的數據量較小,便於檢索,而且也文檔中也省去了設備識別號欄位。
這樣做的負面影響,就是隨著新電站新設備的接入,資料庫中集合的數量會增長。在「--nssize」參數的支持下,MongoDB的命名空間文件(.ns 文件)最大可以為 2G,也就是說最大可以支持約 340 萬個命名,核數據集合中,集合名、」_id」索引、時間索引共需3個命名,即最多接入100萬個左右的設備,這是能滿足業務量要求的。
nnnnnnnnnn核數據集合按以下方式命名:
data.model_xxxx.id_0001ndata.model_xxxx.id_0002ndata.model_yyyy.id_0003ndata.model_yyyy.id_0004n
其中「xxxx」表示設備型號,「0001」表示設備唯一識別號,這樣查詢時利用JavaScript模板字元串可以很方便的拼接對應的集合名。
nnnn在系統整體設計中提到,資料庫中除了核數據集合,其他的業務信息都保存在檔案集合中,按目前的開發需求,主要包含以下三類:
- 信息類,存放類似用戶資料、電站坐標等信息。子集合名以「i_」開頭,例如「file.i_plant_location」。
- 關係類,存放類似電站與設備從屬關係等信息,子集合名以「r_」開頭,例如「file.r_plant_vdev」。
- 映射類,存放數據模型轉換過程中的鍵映射,子集合名以「m_開頭」,例如「file.m_vdev_model」。
除此之外還可能遇到的諸如伺服器日誌、業務工單等類型的數據,按照漸進式的原則,等開發到這一步再設計。
反範式
nnnnnnnn我們知道,在SQL型資料庫表的設計中,要遵循以下範式(Normalization):
- 第一範式(1NF):所有的域都應該是原子性的,即資料庫表的每一列都是不可分割的原子數據項。
- 第二範式(2NF):在1NF的基礎上,非碼屬性必須完全依賴於候選碼。
- 第三範式(3NF):在2NF基礎上,任何非主屬性不依賴於其它非主屬性。
資料庫表按範式設計可以確保數據存儲沒有冗餘信息,節約存儲空間。但是這樣也是的數據查詢時需要進行多表的關聯,使得查詢邏輯複雜,性能降低。
nn在使用MongoDB等NoSQL型資料庫時,往往採取反範式化(Denormalization)的理念,即不按範式的要求設計集合,以數據量的增加換取查詢上的便利。檔案數據集合中存儲的是信息、關係等較為固定的數據,數據量相對較小且一般不會增長,而查詢數據集合卻十分頻繁且要求繁多,故在檔案數據集合設計中可適當減少範式的約束,如電站與虛逆變器從屬關係,就可以如下文檔存儲:
{n plantId: 0001,n vinv: [n {id: 00045, model: XXXX},n {id: 00046, model: XXXX},n {id: 00047, model: YYYY},n {id: 00048, model: YYYY}n ]n}n
這樣相信雖然信息有冗餘,但一次就可取到該電站下所有虛逆變器的id與型號,十分便捷。
映射的持久化與模型動態創建
核數據需轉換為虛設備數據才可供業務功能使用。虛設備數據的模型,是標準的JavaScript對象,其中的成員可以是對象,數據有層級關係,不遵循第一範式,核數據的結構反映實際設備的點表,一般是符合第一範式的。核數據與虛設備數據模型的映射關係持久化保存在資料庫中而不是寫在程序中,這樣才能保證有新型號的設備接入系統後可登記新增。
nnnnnnnnnnnn保存映射關係的對象與虛設備數據模型的對象同構,通過虛設備類型與設備型號聯合索引:
{n vdev: vcom,n model: XXXXn dataMapper: {n pTotal: Ppv,n uTotal: Ipv,n strings: [n {i: Istring1},n {i: Istring2},n {i: Istring3},n {i: Istring4}n ]n }n}n
在後端系統中,odm框架採用mongoose。mongoose通過Schema來反映集合的結構,並以Schema為模板創建與集合對應的Model。每個型號的設備都有獨特的核數據集合結構,考慮到設備型號很多和後續增加新型號的情況,在程序中寫固定的Schema不太合適,我們將構造函數newnmongoose.Schema(definition)中的參數對象持久化保存在資料庫中,需要操作某型號設備的核數據時,通過查找對應的definition對象動態創建Schema,然後通過函數mongoose.model(modelName,nschema, collectionName)動態創建具體單台設備的集合Model。
資料庫安全性
nnnnnnnnnn說道MongoDB,可能很多人會對安全性有所顧慮,2017年1月MongoDB曾遭到大規模的攻擊,被刪除數據勒索比特幣。但這次攻擊並不是MongoDB本身的漏洞造成的,而是因為很多人在使用MongoDB的過程中沒有進行足夠的安全設置。事後網上有很多總結,以下措施可有效提高資料庫的安全性:
- 創建授權用戶,並設置密碼。
- 關閉資料庫的互聯網訪問許可權。
- 修改資料庫默認埠。
- 定期備份數據。
這些措施在傳統資料庫運維中是很常見的,而由於很多人對MongoDB的部署並不熟悉,採用默認設置,開啟MongoDB服務時若不添加任何參數,默認是沒有許可權驗證的,用戶可以遠程訪問資料庫,通過默認埠無需密碼對資料庫任意操作(增、刪、改、查高危動作)。實際搭建和運維資料庫時有意識的進行安全設置就可避免這些攻擊。
推薦閱讀:
※MongoDB資料庫勒索野蠻生長 數量已達27000多個
※Linux安裝mongodb,添加管理員,配置授權和加固
※MongoDB-Elasticsearch 實時數據導入
※微軟校園Hackathon南京站 無駭客 不青春