就像咱們在前一章提到的,一個時間序列是一系列數值,每一個數值都伴隨着一個時間值,表明數據被記錄時的時間。時間序列數據存入後就不多再須要修改了,查詢時常常是查詢一個連續時間段的數據,也可能查詢彙總或者聚合後的數據。時間序列數據庫是一種儲存多個時間序列的方式,在其中檢索一個或幾個時間序列的某一個特定時間段的數據是特別高效的。一樣地,主要用來查詢一個時間段數據的應用程序也適合使用時間序列數據庫來實現。像以前所解釋的,本書的主題是存儲和處理大規模時間序列數據,爲了實現這個目標,首選技術是非關係型NoSQL數據庫,好比Apache HBase或MapR-DB。數據庫
爲大規模時間序列數據庫的實際實現提供務實的建議,是本書的目標,因此咱們須要聚焦於一些能夠簡化和加強真實世界中應用程序發展進程的一些基本步驟。咱們會簡單看看適用於中小型數據集的方法,而後深刻探究咱們主要關注的問題:如何實現大規模TSDB。緩存
爲了獲得一個紮實的實現,有幾種可供選擇的設計方法。如何選擇取決於數據的屬性。有多少種不一樣的時間序列?得到的數據是什麼類型的?使用怎樣的速度採集數據?須要存儲多久的數據?這些問題的答案有助於咱們肯定最優的實現策略。微信
這一章中的主要思想路線數據結構
儘管咱們已經提處處理時間序列數據的一些主要方面,這一章會比以前更深刻地探討存儲和訪問時間序列數據的基本方法。第四章會提供如何使用現有開源軟件來最好地實現這些概念的建議。這兩章有比較多的內容須要理解。而後你就能夠記住若是將這些關鍵的想法集中到一塊兒而不是迷失在細節中,這裏是一個本章內容的一個簡單路線圖:架構
平面文件運維
真正的數據庫:關係型數據庫機器學習
真正的數據庫:非關係型NoSQL數據庫數據庫設計
基本設計ide
可選設計工具
咱們已經回顧了主要思想,如今咱們更詳細地重溫一下而且解釋它們的重要性。
你能夠擴展很是簡單的設計(好比簡單的二維表),使用更聰明的文件格式來使其更先進,好比列存儲的Parquet格式。Parquet是一個有效而且簡單的現代化格式,能夠存儲時間和一些可選值。圖3-1展現了兩種記錄時間序列數據的Parquet schema。左圖中的schema適合你已經知道怎麼合理使用時間序列數據的狀況,它是一個特定場景的存儲方案。例子中,只存了明確指定的4個時間序列的數據(一個存放時間的t和一個存放數據的tempIn組合起來,爲一個時間序列。t和它對應的tempIn、pressureIn、tempOut、pressureOut即4個時間序列),若是須要增長新的,就須要修改schema。右圖中的Parguet schema抽象程度更高,對你想要往文件裏嵌入更多元數據的場景更適合。而且這種格式沒有事先對時間序列的數量作任何限制。若是你想要構建一個給其餘人使用的時間序列庫,右邊的格式會更合適一些。
圖3-1。使用Parquet格式來存儲時間序列數據的兩種可能的schema。左邊的schema使用固定的類型名稱將問題域肯定了。在不改變schema的狀況下,只能夠存儲4個時間序列。相反地,右邊的schema更加靈活,你能夠增長新的時間序列。另外它的抽象層次也更高,把幾個單一的時間序列(一對time、value)按照tags分組,而後放到一個單獨的block中。
這樣的一種時間序列數據(特別是使用相似Parquet格式的狀況)是很是有用的,但前提是你須要分析的時間序列數量相對較小,而且所感興趣的時間範圍相對於單個文件所存儲數據的時間跨度很大(好比每一個文件存放一個月的數據,你查的時候也應該每次查一個月的數據,而不是每次查一天的)。
系統最初使用平面文件來實現是一種很是廣泛的狀況,並且不久以後這種簡單的實現再也不適應快速增加的數據的狀況也是很廣泛的。基本問題是單一文件中的時間序列數量增長了,任何特定的查詢中,真正有用的數據佔所讀取數據的比例就降低了,由於多數讀取到的數據實際上是屬於其餘時間序列的。
一樣地,在文件中的時間跨度比平均查詢的時間範圍已經長不少的狀況,真正有用的數據佔所被讀取數據的比例又降低了,由於文件中的大部分數據已經在你感興趣的時間範圍以外了(好比數據記錄了1個月的數據,而查詢時通常只查某一天的,那爲了定位到這一天,須要先讀大量前邊的實際不須要的數據)。努力解決這些問題的同時通常又會引入其餘的問題。使用大量的文件來確保每一個文件中只有較少的時間序列,會使文件數量大幅增加。一樣地,減小每一個文件所存儲數據的時間範圍會使得文件數量翻倍增加。當在一個相似Apache Hadoop中HDFS的文件系統存儲數據時,大量的文件會致使嚴重的穩定性問題。基於Hadoop的上層系統,如MapR能夠輕鬆處理大量的文件,但檢索和管理大量的小文件也是很低效的,由於須要增長不少搜索時間。
爲了不這些問題,很天然的一步是轉而使用某些形式的真正的數據庫來存儲這些數據。選擇合適的數據庫的方法並非顯而易見的,但基於數據庫的類型和它的設計方案,你有幾個選項。咱們會研究這些問題來幫助你做選擇。
即便是通過良好分區的平面文件,在處理大規模時間序列數據時也會力不從心,因此你也行會考慮使用某些類型的真正的數據庫。當第一次在數據庫中存儲時間序列數據時,使用所謂的星型模式(star schema)設計,而且將數據存放到RDBMS是個很誘人的選擇。在這樣一種數據庫設計中,核心數據存放在事實表(fact table),就像圖3-2展現的那樣。
圖3-2。將時間序列數據存放到RDBMS的一個事實表的設計。其中存放了時間(TIme列)、序列ID(Time series ID列)和數值(Value列)三列。序列的細節存放在維表(dimension table)中(這一對Time、Value是一個時間序列,但這個時間序列的細節,好比Value的含義是什麼,存放在另外一張表中,可使用Time series ID去那個表查)。
在星型模式中,一個表存儲主要的數據,而且會引用其餘表(維表)。該設計一個核心假定是維表要相對小巧,並且不常變更。圖3-2中的時間序列事實表裏,惟一被引用的維表,就是存放這個時間序列詳細信息的維表,它的內容是表中數據(Value列)的含義。好比,若是咱們的時間序列數據是從一個工廠的泵或者其餘設備從採集的,咱們會但願在獲取這個泵的多個維度的數據,如入口和出口的壓強和溫度、泵在不一樣頻段的震動和泵自身的溫度等。這其中的每一個泵的每個維度,都是一個單獨的時間序列,每一個時間序列會有相似泵的序列號、位置、商標、型號等信息,這些信息都存放在維表中。
實際上一些應用程序已經使用像這樣的星型模式來存放時間序列數據了。咱們在多數NoSQL數據庫中也可使用這樣的設計。星型模式解決了有大量不一樣時間序列的問題,在數據點的規模達到數億甚至數十億的狀況下也能夠工做得很好。然而就像咱們在第一章中看到的,即便是19世紀的航運數據也會產生上十億的數據點。在2014年,納斯達克證券交易所在過去三個月就會處理十億規模的交易量。記錄一箇中型計算機集羣的運行環境的話,一天會產生五億的數據點。
而且簡單地將這些數據存儲起來是一回事,對其檢索和處理就是另外一回事了。現代的應用程序如機器學習系統甚至狀態顯示系統都須要每秒檢索和處理上百萬的數據點。
雖然RDBMS能夠擴展到這些大小、速度需求的下限,但帶來的消耗和引入的複雜性會急劇上升。隨着數據規模的繼續增加,基於RDBMS的應用程序愈來愈不適合處理這樣規模的時間序列數據了。使用星型模式但轉而使用NoSQL數據庫的話,也沒有特別的幫助,由於這個問題的核心是星型模式帶來的,而不僅是數據量。
星型模式所觸及的核心問題是每次測量都要使用一行。一個增長時間序列數據庫中數據檢索速度的技術是在每一行存儲不少數值。在一些像Apache HBase或者MapR-DB的NoSQL數據庫中,列的數量幾乎是不受限制的,只要任何特定一行中有數據的列的數量在幾十萬以內。這種能力能夠被用來在每行存放多個數值。這樣作的話,數據點就能夠被更高速地檢索,由於掃描數據的最大速度部分取決於須要掃描的行的數量,部分取決於待檢索數據點的總數,部分取決於待檢索數據的總量。減小行的數量,就大幅減小了一部分檢索開銷,檢索速度就提高了。圖3-3展現了使用寬表來減小時間序列數據行數量的一種方式。這個技術和OpenTSDB(一個開源的數據庫,咱們會在第四章詳細講到)之中使用的默認表結構很類似。須要注意這樣的表設計,和那些須要提早定義詳細schema的系統的表設計是很不同的。有一件事情,若是你想把schema寫下來,那將異常龐大。
圖3-3,在NoSQL時間序列數據庫中一個寬表的使用。關鍵的結構是直觀的,在真正的應用程序中,使用的會是一個二進制的格式,但這樣順序的屬性是同樣的。
由於HBase和MapR-DB都是按照主鍵的順序來存儲數據,圖3-3中的鍵設計會致使每行包含一小段時間的數據在磁盤上是連續存儲的(由於Row key是按時間順序增加,HBase和MapR-DB是按列族存放數據的,Data values中的數據就會所有按照時間順序存放在磁盤上)。這個設計意味着檢索一個特定時間段的數據,涉及的主要是順序磁盤操做,就會比數據按行分散開的狀況快不少。爲了從這個表結構得到性能優點,每一個時間窗口的採樣點要足夠富裕,這樣就能夠減小行的數量,從而提高檢索速度。典型狀況,時間窗口會被調整成每一行包括100-1000採樣點的樣子。
圖3-3中的表設計能夠繼續改進,經過將一行中的全部數據壓縮成一個單一的被稱做blob的數據結構。Blob能夠高度壓縮,因此須要從磁盤讀取的數據量就更少了。而且,若是使用HBase來存儲時間序列數據,每行只有一列的狀況會減小了每列數據在HBase所使用的磁盤文件格式上的開銷,這樣又進一步提升了性能。圖3-4的混合式表結構中,一些行的數據已經被壓縮,另外一些行沒有。
圖3-4。在混合模式設計中,行中的數據能夠被存儲成一個單一的數據結構(blob)。注意實際壓縮的數據更多是二進制的格式。這裏使用JSON格式顯示是爲了更容易理解。
圖3-3中的寬表格式能夠進化成圖3-4的壓縮格式(blob樣式),只要確保那些被壓縮的行對應的時間窗口不會或者不多再有新增的數據。通常地,一旦時間窗口結束後,新的數據就不屬於這個時間窗口了,而後對這個時間窗口中數據的壓縮就能夠開始了。由於在同一行中,已壓縮和未壓縮的數據能夠共存,若是在對行壓縮以後,又有新數據過來了,能夠再簡單地從新壓縮這一行,將新數據合併進來。
圖3-5展現的是概念上的混合式時間序列數據庫的數據流。
在後臺將數據從舊格式轉換成blob格式,會讓renderer(圖3-5中所顯示的)檢索數據並繪製出來的速度有質的提高。例如,在4個節點的MapR集羣中,數據以壓縮格式存放的話,3千萬的數據點能夠在大概20秒內被檢索、聚合、繪製出來。
圖3-5。混合式時間序列數據庫的數據流。數據從數據源到達catcher,而後被插入到NoSQL數據庫中。以後blob maker在後臺定時將數據壓縮成blob格式。數據由renderer檢索和格式化。
壓縮舊數據依然存在一個性能瓶頸。由於數據以未壓縮的格式插入進來,每一個數據點到來後都須要對行作一個更新來將數值插入到數據庫中。對行的更新操做會限制數據的插入速度到每一個集羣中的每一個節點上只有每秒2萬個數據點。
另外一方面,圖3-6中的blob直寫方式的數據流容許插入速度增長了大概1千倍。爲何blob直寫方式會帶來如此大的性能提高?基本的區別是blob maker被轉移到catcher和NoSQL時間序列數據庫之間了。使用這種方式,blob maker就能夠從內存的數據緩存中直接讀取輸入的數據,而不是從存儲層的寬表中提取以前已經被寫入進去的數據。
基本的思想是數據到達後先被存放在內存中。這些數據同時也被寫入到日誌文件中。這些日誌文件就是圖3-6中的restart logs,它們是在Hadoop系統存放的平面文件,不是存儲層的一部分。Restart logs容許內存中的數據緩存被從新導入,在數據管道必須被重建的時候。
在正常操做中,在時間窗口的末尾,新的內存中數據結構會被建立,如今舊的內存中數據結構就能夠用來建立壓縮的blob而後寫入數據庫了。一旦blob被寫入了,日誌文件就被刪除了。這樣就無需像以前的混合設計中將數據兩次寫入。在圖3-5中的混合設計中,所有的輸入數據流都會逐點寫入到存儲層,而後再被blob maker讀取。讀的狀況和寫大體同樣。一旦數據被壓縮成了blob,它又被寫入到數據庫中。相反地,在圖3-6的blob直寫設計的數據流中,完整的數據流只寫入到內存中(這樣速度很快),而不是寫入到數據庫中。數據在壓縮成blob以前不會被寫入到數據庫,因此寫入速度大幅提高。數據庫操做的次數從以前數據點的數量變成了blob的數量,很容易將次數減小到以前幾千分之一這樣的量級。
圖3-6。Blob直寫方式的數據流。Catcher在內存中暫存數據,而且將其寫入到restart logs中。Blob maker週期地從緩存中讀取數據,而後將壓縮成的blob寫入到數據庫中。這個設計的性能提高來自於renderer能夠同時從內存和數據庫中獲取數據。
blob直寫方式的優點是什麼?一個真實世界的例子展現了它能夠作什麼。使用了這個架構,僅使用了一個10節點的MapR集羣中的4個節點,就能夠實現每秒往MapR-DB的表中插入超過一億的數據點。這些節點都有着很高的性能,其中每一個節點有15個CPU核、大量內存和12塊高配置磁盤,但你使用多數硬件均可以達到這個性能級別的1/5到1/2。
這個性能級別聽起來是用來處理海量數據的,可能超出了咱們所須要的處理能力,可是在第五章咱們會展現爲何這樣的性能是很是有用的,即便是對那些相對溫和的應用程序。
在這一點,詢問爲何一個關係型數據庫不能處理和使用混合模式的MapR-DB或者HBase所能承受的插入和分析數據的負載是公平的。當只有blob數據被插入而不使用寬表的狀況,這個問題特別有趣,由於現代關係型數據庫一般支持blob或者array類型。
這個問題的答案是,關係型數據庫主要解決的問題不是提升插入和檢索數據的速度,它如今這樣運行是有其合理性的。使用關係型數據庫的主要緣由也不是由於它有更好的性能。若是使用關係系型數據庫的blob格式存儲數據,就意味着須要放棄大多數其餘好處。此外,SQL沒有提供一個好的抽象方法,來隱藏訪問blob格式數據中的細節。SQL不能用任何合理的方式來訪問這些數據,而且像多行事務等特性也徹底派不上用場了。事務在這裏還會成爲問題,由於即便不使用,它也會成爲一種消耗。一個關係型數據庫須要知足多行事務的需求,這使它更難被擴展到多個節點上。儘管使用如Oracle的高成本數據庫能夠在單個節點實現很高的性能。而使用相似Apache Hbase或者MapR-DB的NoSQL數據庫,你能夠簡單地經過加硬件的方式實現更高的性能。
爲本身用不到的特性買單的模式在一些高性能系統中是存在的。爲了可擴展而犧牲傳統關係型數據庫的一些固有特性也是常見的,但即便你這樣作了,仍是得不到本身想要的擴展性。在這種狀況,使用相似HBase或者MapR-DB的替代方案是有實質上的好處的,由於你同時獲得了性能和可擴展性。
這些寬表、blob混合的表設計是很是誘人的。它們所許諾的巨大性能級別使人興奮,並且它們能運行在有容錯機制、基於Hadoop的系統(好比MapR),從運維的角度看也是很吸引人的。這些新方法都不是空想,它們已經被構建出來,而且被證實有着驚人的結果。然而咱們在這裏呈現的,很大程度都是概念上的東西。有真正已經實現的嗎?下一章咱們會講到如何使用OpenTSDB(一個開源時間序列數據庫工具)和幾個開源的MapR擴展,來實現這些新的設計。結果是利用本章所描述的概念以達到高性能的時間序列數據庫是現代使用場景所須要的。
付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活訂價,歡迎諮詢,微信 ly50247。