1-apache druid架構、原理、執行流程


1、前言

  從druid的0.11版本開始,我就開始關注它,每一次的版本的更新,druid都會使用戶體驗、性能更好,從以前手寫配置文件到可視化的界面操作,從實時節點進行任務提交到現在的索引服務等

  流處理:

      日志監控(Flume) ----> 消息中間件(kafka、MQ)  ----> 流處理(spark streaming/Flink)

      為了使指標開發更加簡單,我們一般不會直接采用Spark core或者Flink DataStream API進行計算,譬如很多時候都會通過樣例類的方式,將其轉化為table,然后利用SQL進行指標開發。

      但是每一次的版本升級,我們如果要添加一些新的指標,那么只能更改原始代碼或者重新跑一個任務

      並且有的時候,數據分析師往往需要的是自主查詢,自己定義指標、自己去開發指標!!

  由於以上的困境,druid幫我們解決了很大一部分,當然它並非完美,在后面我會提到!

  在此之前,我先拋出兩個概念:

    OLTP:

          對數據的增刪改查等操作,主要用於傳統的關系數據庫。

    OLAP:

          數據按不同維度的聚合,維度的下鑽,上卷等,主要用於數據倉庫。

2、druid

      概念:
          主要是解決低延遲下實時數據攝入與查詢的平台,本質是一個數據存儲,但是數據仍然是保存在(hdfs、文件系統等)中。

      特點:

          ①列式存儲格式:

            可以將列作為索引,為僅查看幾列的查詢提供了巨大的速度提升

          ②高可用、高並發:

          ①集群擴展、縮小、刪除、宕機都不會停止服務,全天候運行
          ②HA、sql的並行化執行、可擴展、容災等
          ③支持1000+的並發用戶,並提供隔離機制支持多租戶模式(多租戶就是並發互不影響)

          ④低延遲

             Druid采用了列式存儲、倒排索引、位圖索引等關鍵技術,能夠在亞秒級別內完成海量數據的過濾、聚合以及多維分析等操作。

          ⑤存儲時候聚合:

             無論是實時數據消費還是批量數據處理,Druid在基於DataSource結構存儲數據時即可選擇對任意的指標列進行聚合操作


             聚合:提前做好sum,count等操作

3、druid架構

    官網圖:

        

 

 

    總體可以分為:四個節點 +  三個依賴

    四個節點:

        實時節點(Realtime Node):

            實時攝入數據,對於舊的數據周期性的生成segment數據文件,上傳到deep storage中

            為了避免單點故障,索引服務(Indexer)的主從架構已經逐漸替代了實時節點,所以現在的實時節點,其實里面包含了很多角色:

              作用:可以通過索引服務的API,寫數據導入任務,用以新增、刪除、合並Segment等。是一個主從架構:


              統治節點(overlord):

                              類似於Yarn ResourceManager : 負責集群資源的管理和分配

                             監視數據服務器上的MiddleManager進程,將提取任務分配給MiddleManager

              中間管理者(middle manager):

                              類似於Yarn NodeManager : 負責單個節點資源的管理和分配

                              新數據提取到群集中的過程。他們負責從外部數據源讀取並發布新的段

              苦工(peon):

                               類似於Yarn container :負責具體任務的執行

                                Peon進程是由MiddleManagers產生的任務執行引擎。
                                每個Peon運行一個單獨的JVM,並負責執行單個任務。

                                Peon總是與生成它們的MiddleManager在同一主機上運行              

              Router(路由:可選):

                                可在Druid代理,統治節點和協調器之前提供統一的API網關

             注:統治節點和中間管理者的通信是通過zookeeper完成的


         歷史節點(Historical Node):

             加載已生成的segment數據文件,以供數據查詢

             啟動或者受到協調節點通知的時候,通過druid_rules表去查找需要加載的數據,然后檢查自身的本地緩存中已存在的Segment數據文件,
             然后從DeepStorage中下載其他不在本地的Segment數據文件,后加載到內存!!!再提供查詢。

         查詢節點(Broker Node):

             對外提供數據查詢服務,並同時從實時節點與歷史節點查詢數據,合並后返回給調用方

               緩存:
              外部:第三方的一些緩存系統
              內部:在歷史節點或者查詢節點做緩存

         協調節點(Coodinator Node):

            負責歷史節點的數據負載均衡,以及通過規則(Rule)管理數據的生命周期

            ①通過從MySQL讀取元數據信息,來決定深度存儲上哪些數據段應該在那個歷史節點中被加載,
            ②通過ZK感知歷史節點,歷史節點增加,會自動分配相關的Segment,歷史節點刪除,會將原本在這台節點上的Segment分配給其他的歷史節點

            注:Coordinator是定期運行的,並且運行間隔可以通過配置參數配置

    三個依賴:

        1) Mysql:

            存儲關於Druid中的metadata,規則數據,配置數據等,
            主要包含以下幾張表:
              "druid_config”(通常是空的),
              “druid_rules”(協作節點使用的一些規則信息,比如哪個segment從哪個node去load)
              “druid_segments”(存儲 每個segment的metadata信息);

        2 )Deep storage:

            存儲segments,Druid目前已經支持本地磁盤,NFS掛載磁盤,HDFS,S3等。

        3) ZooKeeper:

            ①查詢節點通過Zk來感知實時節點和歷史節點的存在,提供查詢服務。
            ②協調節點通過ZK感知歷史節點,實現負載均衡
            ③統治節點、協調節點的lead選舉

4、實時Segment數據文件的流動:

      生成:

        ①實時節點(中間管理者)會周期性的將同一時間段生成的數據合並成一個Segment數據文件,並上傳到DeepStorage中。        

        ②Segment數據文件的相關元數據信息保存到MetaStore中(如mysql,derby等)。        

        ③協調節點定時(默認1分鍾)從MetaSotre中獲取到Segment數據文件的相關元信息后,將按配置的規則分配到符合條件的歷史節點中。        

        ④協調節點會通知一個歷史節點去讀        

        ⑤歷史節點收到協調節點的通知后,會從DeepStorage中拉取該Segment數據文件到本地磁盤,並通過zookeeper向集群聲明可以提供查詢了。

        ⑥實時節點會丟棄該Segment數據文件,並通過zookeeper向集群聲明不在提供該Sgment的查詢服務。               //其實第四步已經可以提供查詢服務了
        ⑦而對於全局數據來說,查詢節點(Broker Node)會同時從實時節點與歷史節點分別查詢,對結果整合后返回用戶。

      查詢:

        查詢首先進入Broker,按照時間進行查詢划分
        確定哪些歷史記錄和 MiddleManager正在為這些段提供服務
        Historical / MiddleManager進程將接受查詢,對其進行處理並返回結果

5、DataSource

      Druid中的數據存儲在被稱為datasource中,類似RDMS中的table!!!

      

 

 

      每個datasource按照時間划分。每個時間范圍稱為一個chunk(一般都是以天分區,則一個chunk為一天)!!! //也可以按其他屬性划分

      在chunk中數據被分為一個或多個segment,每個segment都是一個單獨的文件,通常包含幾百萬行數據

      注:這些segment是按照時間組織成的chunk,所以在按照時間查詢數據時,效率非常高。

 

      數據分區:

        任何分布式存儲/計算系統,都需要對數據進行合理的分區,從而實現存儲和計算的均衡,以及數據並行化。

        而Druid本身處理的是事件數據,每條數據都會帶有一個時間戳,所以很自然的就可以使用時間進行分區。

        為什么一個chunk中的數據包含多個segment!!!????原因就是二級分區

      二級分區:

        很可能每個chunk的數據量是不均衡的,而Duid為了解決這種問題,提供了“二級分區”,每一個二級分區稱為一個Shard(分片)
        其實chunk、datasource都是抽象的,實際的就是每個分區就是一個Shard,每個Shard只包含一個Segment!!!,因為Segment是Shard持久化的結果

        Druid目前支持兩種Shard策略:

        Hash(基於維值的Hash)
        Range(基於某個維度的取值范圍)

        譬如:

          2000-01-01,2000-01-02中的每一個分區都是一個Shard
          2000-01-02的數據量比較多,所以有兩個Shard,分為partition0、partition1。每個分區都是一個Shard

          Shard經過持久化之后就稱為了Segment,Segment是數據存儲、復制、均衡(Historical的負載均衡)和計算的基本單元了。
          Segment具有不可變性,一個Segment一旦創建完成后(MiddleManager節點發布后)就無法被修改,
          只能通過生成一個新的Segment來代替舊版本的Segment。  

       Segment內部存儲結構:

          Segment內部采用列式存儲          //並不是說每列都是一個獨立的文件,而是說每列有獨立的數據結構,所有列都會存儲在一個文件中

          Segment中的數據類型主要分為三種:

            時間戳
            維度列
            指標列

          對於時間戳列和指標列,實際存儲是一個數組

          對於維度列不會像指標列和時間戳這么簡單,因為它需要支持filter和group by:

          所以Druid使用了字典編碼(Dictionary Encoding)和位圖索引(Bitmap Index)來存儲每個維度列。每個維度列需要三個數據結構:

              1、需要一個字典數據結構,將維值(維度列值都會被認為是字符串類型)映射成一個整數ID。
              2、使用上面的字典編碼,將該列所有維值放在一個列表中。
              3、對於列中不同的值,使用bitmap數據結構標識哪些行包含這些值。       //位圖索引,這個需要記住

              注:使用Bitmap位圖索引可以執行快速過濾操作(找到符合條件的行號,以減少讀取的數據量)


          Druid針對維度列之所以使用這三個數據結構,是因為:

              使用字典將字符串映射成整數ID,可以緊湊的表示結構2和結構3中的值。
              使用Bitmap位圖索引可以執行快速過濾操作(找到符合條件的行號,以減少讀取的數據量),因為Bitmap可以快速執行AND和OR操作。
              對於group by和TopN操作需要使用結構2中的列值列表

              實例:

                1. 使用字典將列值映射為整數
                  {
                    "Justin Bieher":0,
                    "ke$ha":1
                  }
                2. 使用1中的編碼,將列值放到一個列表中

                  [0,0,1,1]

                3. 使用bitmap來標識不同列值

                  value = 0: [1,1,0,0] //1代表該行含有該值,0標識不含有
                  value = 1: [0,0,1,1]

                  因為是一個稀疏矩陣,所以比較好壓縮!!
                  Druid而且運用了Roaring Bitmap能夠對壓縮后的位圖直接進行布爾運算,可以大大提高查詢效率和存儲效率(不需要解壓縮)

       Segment命名:

              如果一個Datasource下有幾百萬個Segment文件,我們又如何快速找出我們所需要的文件呢?答案就是通過文件名稱快速索引查找。

              Segment的命名包含四部分:

                  數據源(Datasource)、時間間隔(包含開始時間和結束時間兩部分)、版本號和分區(Segment有分片的情況下才會有)。

                  eg:wikipedia_2015-09-12T00:00:00.000Z_2015-09-13T00:00:00.000Z_2019-09-09T10:06:02.498Z

                    wikipedia: Datasource名稱
                    開始時間: 2015-09-12T00:00:00.000Z      //該Segment所存儲最早的數據,時間格式是ISO 8601
                    結束時間: 2015-09-13T00:00:00.000Z      //該segment所存儲最晚的數據,時間格式是ISO 8601
                    版本號: 2019-09-09T10:06:02.498Z         //此Segment的啟動時間,因為Druid支持批量覆蓋操作,
                                             //當批量攝入與之前相同數據源、相同時間間隔數據時,數據就會被覆蓋,這時候版本號就會被更新
                        分片號: 從0開始,如果分區號為0,可以省略 //分區的表現其實就是分目錄

              注:單機形式運行Druid,這樣Druid生成的Segment文件都在${DRUID_HOME}/var/druid/segments 目錄下
              注:為了保證Druid的查詢效率,每個Segment文件的大小建議在300MB~700MB之間
              注:版本號的意義:

                    在druid,如果您所做的只是追加數據,那么每個時間chunk只會有一個版本。

                    但是當您覆蓋數據時,因為druid通過首先加載新數據(但不允許查詢)來處理這個問題,一旦新數據全部加載,
                    切換所有新查詢以使用這些新數據。然后它在幾分鍾后掉落舊段!!!

        存儲聚合:

              無論是實時數據消費還是批量數據處理,Druid在基於DataSource機構存儲數據時即可選擇對任意的指標列進行聚合操作:

              1、基於維度列:相同的維度列數據會進行聚合
              2、基於時間段:某一時間段的所有行會進行聚合,時間段可以通過queryGranularity參數指定

              聚合:提前做好sum,count等操作

        Segment生命周期:

              在元數據存儲中!每個Segment都會有一個used字段,標記該段是否能用於查詢

              is_Published:

                  當Segment構建完畢,就將元數據存儲在元數據存儲區中,此Segment為發布狀態

              is_available:

                  如果Segment當前可用於查詢(實時任務或歷史進程),則為true。

              is_realtime:

                  如果是由實時任務產生的,那么會為true,但是一段時間之后,也會變為false

              is_overshadowed:

                  標記該段是否已被其他段覆蓋!處於此狀態的段很快就會將其used標志自動設置為false。

下一篇博客會用這個給大家做一個實例,讓大家更好的了解!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM