數據分析 - 時序數據庫

1 海量數據分析

海量數據分析類系統的設計主要面臨2個大問題:html

  • 1 海量數據如何存儲?算法

    • a 藉助於於Hadoop生態體系中的存儲系統或者其餘存儲系統來存儲海量數據,自身提供對上述數據的分佈式查詢分析功能,如Impala、Hive、SparkSQL、Presto、Drill、Kylin、OpenTSDB等

    優點和劣勢:數據庫

    加入了Hadoop體系的生態圈,更加容易被接受,同時省去了研發分佈式存儲系統的麻煩,更多的是在分佈式查詢上作優化。但沒法在存儲上作更加深度的優化,好比沒有倒排索引支持,過濾查找速度可能相對弱些,後面會重點分析下OpenTSDB的困局。網絡

    • b 自身提供分佈式存儲,如Elasticsearch、Druid、ClickHouse、Palo

    優點和劣勢:多線程

    能夠在存儲上進行深度的優化,爲本身特定數據模型進行定製。代價就是本身要實現分佈式存儲架構

  • 2 如何對海量數據進行快速分析?elasticsearch

    • a 對Hadoop上的原始數據集進行大規模並行的分析處理,如SQL on Hadoop之類的Hive、SparkSQL、Impala等等。經過並行的內存計算分析來提升查詢速度。分佈式

    • b 預聚合之類的系統,如Kylin、Druid等。有效減小了查詢的數據量,提升了查詢速度,可是丟失了原始數據。Druid中的預聚合只是在時間維度進行預聚合,其餘維度上的聚合在查詢時計算獲得,而Kylin會根據用戶配置計算出全部要聚合的維度,這個聚合量就大了不少。最終佔用的磁盤空間也相對比較大,查詢速度相對來講快。ide

    • c 含有列式存儲、倒排索引等特性之類的系統,如Elasticsearch、Druid、InfluxDB。如倒排索引能夠高效的進行數據過濾,進而能夠提升查詢的速度。還有不少特性如CBO和Vectorization、查詢的流式處理等查詢方面的優化因爲篇幅暫不在本文的討論範圍內,本文注重於存儲層面對查詢的影響。oop

2 時序數據庫

時序數據庫也屬於海量數據分析的範疇內,典型的系統如InfluxDB、Druid、OpenTSDB、Prometheus,也有人拿Elasticsearch來作時序數據庫(好比騰訊)。具體有如下幾個顯著的場景特色:

  • 1 目前的幾個時序數據庫的數據模型基本統一

    Metric:指標名稱

    Timestamp:時間戳

    Tags:維度組合

    Fields:指標值

    相對關係型數據庫的數據模型來講,區分就是

    • a 一定含有時間字段
    • b 區分列的類型,須要用戶將列劃分爲維度列和指標列。

    劃分後的好處就是:能夠作一些存儲上的優化,好比自動對維度列創建倒排索引,不須要用戶使用關係型數據庫那樣針對某一列或者多列手動創建索引。總的來講:雖然給用戶帶來了分區維度列和指標列的麻煩,可是帶來的收益確實很是大的。

  • 2 須要支持海量數據的實時寫入與查詢

    時序數據庫使用最多的場景就是監控領域,訂單量的實時監控、機器的CPU、內存、網絡等實時監控

    要作到這點目前來講基本上須要實現一個LSM樹存儲模型,上述幾個時序數據庫基本都有。

  • 3 在查詢數據時都有時間範圍

    在存儲的設計上都會按照時間進行分片存儲,按照時間範圍查詢時能夠快速過濾掉無關的數據。

下面就簡述下目前的幾個時序數據庫如何去解決前面提到海量數據分析的2大問題。

2.1 OpenTSDB

  • 1 海量數據如何存儲?

    OpenTSDB自身並不實現分佈式存儲,而是藉助於HBase或者Cassandra來存儲海量的數據。

    對於實時寫入也不是問題。

  • 2 如何對海量數據進行快速分析?

    • a OpenTSDB在數據過濾能力上仍是比較弱的,主要是由於它所依賴的底層存儲HBase自身目前暫時不支持二級索引(實際上有辦法支持可是這其實就不在OpenTSDB考慮的範圍內了)或者倒排索引。

    • b 在預聚合方面,OpenTSDB也是比較弱的。OpenTSDB提的2個概念,Rollup和Pre-Aggregates。Rollup是將同一個維度組合Series不一樣時間點上的數據聚合起來,解決查詢時間範圍大的問題,Pre-Aggregates是將不一樣的Series在同一時間點上的數據聚合起來,解決維度過多的問題。

      OpenTSDB要實現這2大功能,若是底層存儲支持聚合操做,那麼OpenTSDB中的TSD就能夠將部分聚合的數據發往底層存儲,由底層存儲完成最終的聚合,這裏顯然底層存儲HBase並不支持聚合而且可能不會爲了OpenTSDB來實現聚合功能。因此OpenTSDB中的TSD就只能先聚合好最終的數據而後再發往底層存儲。TSD本來是無狀態設計,每一個TSD只能見到部分的數據,這是沒法完成上述任務的。若是將相同Metric的數據路由到同一臺TSD,又會帶來不少問題,同時會有時間窗口的引入,當數據在該時間窗口內未達到就會被丟棄。OpenTSDB又只能寄但願外部的流式處理來完成聚合任務。上述的諸多問題最根本的緣由就是底層存儲不支持聚合。這一問題詳見OpenTSDB的Rollup和Pre-Aggregates

    • 在時間範圍過濾上也仍是能定位到startKey和endKey,也能作到快速過濾。

OpenTSDB依賴了其餘存儲系統,在數據規模小的時候還能忍受,數據規模大了,查詢速度就慢不少,想改變卻又因底層存儲不支持本身特定需求的緣由而困難重重。

不過OpenTSDB將metric、tagk、tagv轉換成id的方式確實能夠省去不少的存儲容量,這部分值的借鑑。

2.2 InfluxDB

  • 1 海量數據如何存儲?

    InfluxDB是自研存儲,經過hash分片的方式來存儲海量數據。

    對於實時寫入,也是經過LSM方式來實現數據的快速寫入。

  • 2 如何對海量數據進行快速分析?

    • a InfluxDB實現了倒排索引,所以能夠實現快速的數據過濾功能。倒排索引的代價就是下降了寫入速度,再加上倒排索引的佔用量可能會很大,並不能徹底放內存,爲了解決上述問題,倒排索引的寫入也引入了LSM模型,即InfluxDB的TSI。其實InfluxDB的倒排索引的設計並不突出,倒排索引針對數據過濾場景很是有優點,可是對於沒有數據過濾的場景若是仍然沿用倒排索引的查詢方式經過一系列series id去隨機查找數據會很慢很慢,在這方面InfluxDB作的也很差。綜上2方面的因素,InfluxDB在對比Druid、咱們自研的LinDB會都會弱一些。後面會詳細分析下3者的倒排索引實現。

    • b 在預聚合方面,InfluxDB引入了Continuous Query和Retention Policy。2者配合使用能夠提升數據的查詢速度。相比於OpenTSDB中的Rollup和Pre-Aggregates,InfluxDB所有融合到了Continuous Query,既能夠對時間維度進行Rollup,又能夠將多個Series進行Pre-Aggregates。其實現原理很簡單:就是查詢一遍聚合後的數據寫入到新的指標名下或者新的Retention Policy下。這種經過查詢數據來實現預聚合的方式都有一個缺點:每次查詢都是查詢最近一段時間範圍的數據,對於以前已經查詢過的時間範圍若來了新的數據,並不會再次查詢一次更新下結果。

      這種方式的預聚合的確能提升速度,可是對用戶來講干預很大,用戶須要理解Continuous Query,而且根據本身的查詢需求來編寫對應的Continuous Query,在查詢時又要手動去選擇合適的Retention Policy去進行查詢,即還不可以作到自動化,InfluxDB本身又不能對全部metric都自動執行Continuous Query, 由於它不知道該如何聚合,不一樣用戶寫入的metric指標可能有不一樣的聚合需求。

    • c 列式存儲,對於不須要查詢的列能夠顯著下降IO,也是借鑑了Facebook的gorilla論文中的壓縮算法進行壓縮,壓縮比高

    • d 在時間範圍過濾上,因爲InfluxDB存儲自己就是按照時間範圍劃分的,因此也能夠高效過濾

2.3 Druid

  • 1 海量數據如何存儲?

    Druid也是自研存儲,可是這個分佈式存儲的架構設計至關複雜。

    對於實時寫入,也是經過相似LSM方式來實現數據的快速寫入。

  • 2 如何對海量數據進行快速分析?

    • a 實現了倒排索引,在數據過濾方面也是很是高效的,詳見後面的InfluxDB、Druid、咱們自研的LinDB實現對比。

    • b 在時間範圍過濾上,Druid的存儲也是按照時間範圍劃分的,也能達到快速過濾。

    • c 在預聚合上,Druid支持在時間維度上的聚合,解決了部分問題,並無解決維度基數很大時的預聚合問題,而且Druid不支持同一份數據聚合出不一樣粒度的數據(好比segmentGranularity爲10s,對於查詢幾個月的數據量來講10s粒度仍是很慢的)。這就須要用戶本身同一份數據屢次輸入不一樣的datasource(每一個datasource不一樣的segmentGranularity)來解決,查詢時又要根據查詢時間範圍手動來選擇對應粒度的datasource。

2.4 Elasticsearch

這裏只是重點來講Elasticsearch在海量數據的聚合分析領域的應用,這裏並不涉及到全文搜索(這是Elasticsearch立足的主戰場,鮮有對手,可是在海量數據的聚合分析領域Elasticsearch對手就不少)

  • 1 海量數據如何存儲?

    Elasticsearch也是藉助於Lucene擁有本身的存儲,同時也實現了分佈式存儲相關功能。

    對於實時寫入,也是經過LSM方式來實現數據的快速寫入的。

  • 2 如何對海量數據進行快速分析?

    • a 藉助於Lucene實現了倒排索引,在數據過濾方面也是很是高效的。

    • b 在時間範圍過濾上,Elasticsearch因爲其通用性並無針對時間進行特殊優化,致使在這方面相對InfluxDB、Druid遜色一些。雖然能夠經過天天創建一個Index來緩解這個問題,可是仍然有避不開的麻煩,即在設計聚合分析時須要可以支持多個Index,這無疑增長了複雜度,不是一個理想方案。

    • c 在預聚合方面,Elasticsearch在Elastic{ON} 2018實現了這個功能。目前來看實現上和InfluxDB應該是相似的,都是經過定時任務查詢原始數據來實現Rollup,而且支持多時間粒度聚合,來適應不一樣的查詢時間範圍。只是目前該功能還不能自動化,須要用戶參與。

2.5 Kylin

在必定程度上Kylin也會被用作時序方面的數據分析。

  • 1 海量數據如何存儲?

    Kylin依託於HBase(也能夠換成別的存儲)來實現海量數據的存儲。

    對於實時寫入,因爲Kylin的重點是在數據攝入時作了大量的預聚合,那麼就會致使實時性相比其餘的幾個系統仍是慢不少的。

  • 2 如何對海量數據進行快速分析?

    • a 在預聚合方面,Kylin作了不少工做,基本上把全部維度組合都進行預聚合了一遍,大大減小了在查詢時的聚合量,速度相比前面幾個仍是很是快的。拿佔用更多的空間和下降了實時性的代價換取更短的查詢時間。Kylin經過各類方式來減小預聚合的量來下降上述代價,可是這都須要用戶理解並參與優化,增長了用戶的使用負擔。

    • b 在時間範圍過濾上,時間也屬於預聚合中的一個維度,因此時間範圍過濾也是很高效的。

    • c kylin雖說它所依賴的存儲並不支持倒排索引,可是因爲大量的預聚合,在必定程度上已經減小了查詢時要聚合的數據量,因此即便HBase的過濾能力弱最終的速度也仍是ok的。

3 LinDB時序數據庫

在調研了上述諸多系統以後,來看看LinDB的設計

  • 1 海量數據如何存儲?

    LinDB經過借鑑Kafka的集羣功能來實現海量數據的存儲,目前只依賴ZooKeeper,而且在部署方面徹底能夠任意臺部署,並不要求至少3臺。

    對於實時寫入,LinDB內部也是採用LSM方式來實現快速寫入。

  • 2 如何對海量數據進行快速分析?

    • a 實現了倒排索引,在數據過濾方面也是很是高效的,先總結下其餘系統的倒排索引:

      • 總體實現方式

        InfluxDB:基本實現方式是tagKey-tagValue-[seriesKey offset list],做爲全局索引,優勢:在時序場景下,相對文件級別索引,大部分時間每一個文件索引基本上都差很少的,因此在查詢上只須要1次索引查詢便可,不像文件級別索引每一個文件都要進行索引查詢。

        Druid:基本實現方式是tagKey-tagValaue-[row id list],每一個文件包含本身的索引。缺點:每一個文件都要進行索引查詢。優勢:在數據遷移方面很是有利,只須要將整個文件複製便可,而InfluxDB就相對麻煩不少。

        LinDB:基本實現方式是tagKey-tagValue-[series Id list],也是做爲全局索引,優缺點和InfluxDB同樣的。LinDB這樣設計主要仍是基於時序場景下大部分狀況下文件索引都是重複的這一狀況考慮的。

      • 倒排索引大小

        這裏指的是上述一個list的大小

        InfluxDB、LinDB:倒排索引大小就是組合數

        Druid:倒排索引大小就是行數,在時序數據場景下,一個文件中的數據基本是全部組合數多個時間點的數據,數據行數相比組合數大了不少,所以相對InfluxDB和LinDB大了不少

      • 數據存儲大小

        InfluxDB:數據文件中的key是seriesKey+FieldKey,這一部分也是至關耗費存儲的,並無像OpenTSDB那樣將他們轉換成id來存儲。對於時間的壓縮採用差值的方式進行壓縮,對於數據的壓縮採用facebook的gorilla論文中的方式。

        Druid:倒排索引list中存儲的是行號,每一個文件中都將tags轉換成對應的id來存儲,相比InfluxDB也沒有重複存儲大量的tags。對於時間的壓縮和數據都採用LZ4方式進行壓縮

        LinDB:倒排索引list中存儲的是series id,數據文件中是按照id來存儲的,相比InfluxDB沒有重複存儲大量的tags,相比Druid沒有重複存儲tags到id的映射,以及每一個tags的bitmap。所以從總體設計上來看LinDB是最節省存儲的。對於時間的壓縮採用一個bit的方式來表明該數據槽位是否有數據(這種設計幾乎在大部分狀況下是很是有利的,除非是隻有開始和結束槽位有數據,中間槽位都沒來數據,這種特別的場景極少),所以相比InfluxDB和Druid作了極致,對於數據的壓縮採用facebook的gorilla論文中的方式。

      • 查找數據方面

        InfluxDB:先根據過濾條件和倒排索引找到符合條件的全部seriesKey offset,而後再根據offset找到對應seriesKey,再將seriesKey和field拼成數據文件中的key,到符合查找時間範圍的數據文件中查找對應的數據,全部的key分片隨機查找,每一個key查找是二分查找。

        Druid:在每個符合查找時間範圍的文件中,先根據過濾條件和倒排索引找到符合條件的全部row id,再按照全部的row id順序性查找field的數據。比InfluxDB的優點在於:

        1 InfluxDB經過倒排索引找到的是seriesKey offset,還不能根據這個offset直接到數據文件中查找,還必須多一步offset到seriesKey的查找,假如符合條件的series有300萬,那麼這一步就已經至關耗時了,慢也就是必然的了

        2 InfluxDB對全部的seriesKey+field拼接成的key的查找是隨機查找,每一個key的查找是二分查找的方式,假如符合條件的series有300萬,那麼300萬次的二分查找也是至關耗時(而且key的長度可加重了耗時)。InfluxDB能夠對全部的key進行分片多線程查找,相對來講快了一點,可是並不改變查詢效率差的本質。

        LinDB:先根據過濾條件找到符合條件的全部series id,咱們就能夠直接拿着series id到數據文件中查找,相比InfluxDB省了offset到seriesKey的查找過程。Druid是文件內索引,過濾條件出來的結果是row id,自然順序性,而且能夠直接定位到數據位置。LinDB和InfluxDB是全局索引,過濾出來的結果須要通過一個查找過程才能找到對應文件中的offset。InfluxDB是最原始的二分查找,效率並不高。LinDB經過計算要查找的series id是文件中的第幾個series id,就能夠根據這個序號找到對應的offset。咱們經過RoaringBitMap的分桶策略,順序性分片,只須要算出在當前分片的第幾個位置再加上初始位置就能夠獲得總的位置,計算當前分片的第幾個位置是二分查找。

        總的來講:

        InfluxDB: 多線程查找、二分查找、查找是長字符串之間的比較(還多一步series offset到seriesKey的查找)。這就是InfluxDB慢的一部分緣由。

        Druid:單線程查找、O(1)查找、查找是數字之間的比較

        LinDB: 多線程查找、局部二分查找、查找是數字之間的比較

    • b 在時間範圍過濾上,LinDB底層存儲就是按照時間範圍進行物理劃分的,因此能夠快速過濾

    • c 在預聚合上,先總結下其餘系統的預聚合:

      • OpenTSDB的預聚合是它的痛點

      • InfluxDB的預聚合經過查詢來實現有缺陷,以及用戶須要理解Continuous Query和Retention Policy

      • Druid底層存儲支持時間維度的預聚合,而且只能有1種聚合粒度

      • Elasticsearch最近發佈支持預聚合跟InfluxDB實現有點相似

      • Kylin預聚合比較全面,可是須要用戶深度參與優化

      從目前公司內部的實際使用狀況來看,不少用戶就前面所說的時序數據模型都沒有理解,更別期望他們去理解Continuous Query、Kylin Cube等概念了,對於他們來講這些概念他們也不想了解,只管打點便可,剩下的性能問題都是系統維護者的事。因此咱們LinDB面向用戶的首要目標是簡單易用,用戶只需理解時序數據模型便可。

      要實現這個目標並不簡單,查詢的數據量大小主要有2方面的因素:組合數的大小*每一個組合數的點數,好比100個host,每一個host 32個核,每一個host的每一個核 1s一個點,那麼查詢全部host 7天內的所涉及的數據量大小爲 (100*32) * (7*24*3600)。爲了減小這個數據量的大小,就須要從下面2個方向入手:

      • 對時間維度進行預聚合

        提早將每一個組合1s一個點的數據聚合成10s一個點、10分鐘一個點、1天一個點。這樣就能夠將數據量下降至1/十、1/(10*60)、1/(24*3600)。查詢最近幾個小時,能夠用10s粒度的數據區查,查詢幾天的數據能夠用10分鐘粒度的數據區查,查詢幾個月甚至幾年的數據能夠用1天粒度的數據去查,這已經大大下降了要查詢的數據量,意味着查詢幾年的數據的響應時間均可以是毫秒級別的。

        用戶惟一須要作的:用戶須要給出每一個數據即Field的聚合方式便可

        咱們如何實現:10s、10分鐘、1天粒度,這3種粒度能夠自定義,徹底能夠知足從最近幾小時到幾年範圍的數據查詢,用戶在查詢指標時會根據用戶的查詢時間範圍自動選擇合適粒度的數據,佔用的存儲基本是原始數據的1/十、1/(10*60)、1/(24*3600)。這一切都是用戶無感知的。在實現上,咱們不是建了多個庫,1份數據寫到每一個庫中(每一個庫都包含複製、LSM模型),咱們不是像InfluxDB那樣經過查詢來實現,咱們是每一個庫支持多粒度存儲,在寫入時寫入到最小粒度中,flush出多個文件後,將多個文件一塊兒讀取聚合到下一個粒度中。

      • 對組合數進行預聚合

        提早將每一個host的全部核聚合起來,可是這是跟用戶的查詢需求密不可分的,仍是須要用戶參與。因爲時間維度預聚合已經基本知足了咱們的需求,因此這個目前咱們還暫時沒作,以後咱們可能會在時間維度預聚合的結果上再來實現這一功能,將完全解決大時間範圍大維度查詢的問題。

      咱們能夠看到InfluxDB、Kylin等Rollup的實現是將時間維度預聚合和組合數預聚合合併在一個功能中,雖然簡化了開發,可是卻麻煩了用戶。咱們則必需要把他們分開,咱們將時間維度預聚合徹底自動化,使得幾乎全部的用戶不用陷入如何優化的煩惱中,針對極個別的用戶咱們以後再經過組合數預聚合讓用戶參與優化。總之,LinDB在功能設計上都以簡化用戶使用爲目標。

特別有意思的是不少系統在本身特定領域站穩腳跟後都會進行擴張到其餘相關領域,好比OLTP的數據庫向OLAP擴張,再好比Elasticsearch在全文搜索領域站穩腳跟後擴張到時序數據庫領域,若是在設計之初就能考慮到其領域的關鍵點的話,那麼擴張可能會順利不少,好比時序數據庫中很是重要的倒排索引和預聚合

將來對數據的實時寫入和實時查詢要求會愈來愈高,所以時序數據庫相比依賴於HDFS的Hive、SparkSQL、Impala等優點很大,可是目前時序數據庫的查詢豐富性方面相比它們還差不少,一般都是對單指標的filter和group by查詢,好比對多指標的join暫時暫時都不支持。在監控場景下,join的需求不是很強烈,可是時序數據庫要想走向其餘場景下的數據分析領域,瓜分他們的地盤,join仍是必不可少的功能,這時對時序數據庫的分佈式SQL查詢要求也就變高了,若是能作到的話才更容易走出時序數據分析領域,向其餘數據分析領域進軍

參考文檔:

OpenTSDB文檔

InfluxDB文檔

InfluxDB系列解析

Druid文檔

Druid Storage 原理

Elasticsearch Rollups

Elasticsearch技術研討知乎專欄

Roaring Bitmaps

相關文章
相關標籤/搜索