Druid高效架構(gòu)

我們知道Druid能夠同時提供對大數(shù)據(jù)集的實時攝入和高效復雜查詢的性能,主要原因就是它獨到的架構(gòu)設計和基于Datasource與Segment的數(shù)據(jù)存儲結(jié)構(gòu)。接下來我們會分別從數(shù)據(jù)存儲和系統(tǒng)節(jié)點架構(gòu)兩方面來深入了解一下Druid的架構(gòu)。

數(shù)據(jù)存儲

Druid將數(shù)據(jù)組織成Read-Optimized的結(jié)構(gòu),而這也是Druid能夠支持交互式查詢的關(guān)鍵。Druid中的數(shù)據(jù)存儲在被稱為datasource中,類似RDMS中的table。每個datasource按照時間劃分,如果你有需求也可以進一步按其它屬性劃分。每個時間范圍稱為一個chunk(比如你按天分區(qū),則一個chunk為一天)。在chunk中數(shù)據(jù)由被分為一個或多個segment(segment是數(shù)據(jù)實際存儲結(jié)構(gòu),Datasource、Chunk只是一個邏輯概念),每個segment都是一個單獨的文件,通常包含幾百萬行數(shù)據(jù),這些segment是按照時間組織成的chunk,所以在按照時間查詢數(shù)據(jù)時,效率非常高。

image

數(shù)據(jù)分區(qū)

任何分布式存儲/計算系統(tǒng),都需要對數(shù)據(jù)進行合理的分區(qū),從而實現(xiàn)存儲和計算的均衡,以及數(shù)據(jù)并行化。而Druid本身處理的是事件數(shù)據(jù),每條數(shù)據(jù)都會帶有一個時間戳,所以很自然的就可以使用時間進行分區(qū)。比如上圖,我們指定了分區(qū)粒度為為天,那么每天的數(shù)據(jù)都會被單獨存儲和查詢(一個分區(qū)下有多個Segment的原因往下看)。
使用時間分區(qū)我們很容易會想到一個問題,就是很可能每個時間段的數(shù)據(jù)量是不均衡的(想一想我們的業(yè)務場景),而Duid為了解決這種問題,提供了“二級分區(qū)”,每一個二級分區(qū)稱為一個Shard(這才是物理分區(qū))。通過設置每個Shard的所能存儲的目標值和Shard策略,來完成shard的分區(qū)。Druid目前支持兩種Shard策略:Hash(基于維值的Hash)和Range(基于某個維度的取值范圍)。上圖中,2000-01-01和2000-01-03的每個分區(qū)都是一個Shard,由于2000-01-02的數(shù)據(jù)量比較多,所以有兩個Shard。

Segment

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

Segment內(nèi)部存儲結(jié)構(gòu)

接下來我們可以看下Segment文件的內(nèi)部存儲結(jié)構(gòu)。因為Druid采用列式存儲,所以每列數(shù)據(jù)都是在獨立的結(jié)構(gòu)中存儲(并不是獨立的文件,是獨立的數(shù)據(jù)結(jié)構(gòu),因為所有列都會存儲在一個文件中)。Segment中的數(shù)據(jù)類型主要分為三種:時間戳、維度列和指標列。

image

對于時間戳列和指標列,實際存儲是一個數(shù)組,Druid采用LZ4壓縮每列的整數(shù)或浮點數(shù)。當收到查詢請求后,會拉出所需的行數(shù)據(jù)(對于不需要的列不會拉出來),并且對其進行解壓縮。解壓縮完之后,在應用具體的聚合函數(shù)。
對于維度列不會像指標列和時間戳這么簡單,因為它需要支持filter和group by,所以Druid使用了字典編碼(Dictionary Encoding)和位圖索引(Bitmap Index)來存儲每個維度列。每個維度列需要三個數(shù)據(jù)結(jié)構(gòu):

  1. 需要一個字典數(shù)據(jù)結(jié)構(gòu),將維值(維度列值都會被認為是字符串類型)映射成一個整數(shù)ID。
  2. 使用上面的字典編碼,將該列所有維值放在一個列表中。
  3. 對于列中不同的值,使用bitmap數(shù)據(jù)結(jié)構(gòu)標識哪些行包含這些值。

Druid針對維度列之所以使用這三個數(shù)據(jù)結(jié)構(gòu),是因為:

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

我們以上面"Page"維度列為例,可以具體看下Druid是如何使用這三種數(shù)據(jù)結(jié)構(gòu)存儲維度列:

1. 使用字典將列值映射為整數(shù)
{
"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]

下圖是以advertiser列為例,描述了advertiser列的實際存儲結(jié)構(gòu):

image

前兩種存儲結(jié)構(gòu)在最壞情況下會根據(jù)數(shù)據(jù)量增長而成線性增長(列數(shù)據(jù)中的每行都不相同),而第三種由于使用Bitmap存儲(本身是一個稀疏矩陣),所以對它進行壓縮,可以得到非常客觀的壓縮比。Druid而且運用了Roaring Bitmap(http://roaringbitmap.org/)能夠?qū)嚎s后的位圖直接進行布爾運算,可以大大提高查詢效率和存儲效率(不需要解壓縮)。

Segment命名

高效的數(shù)據(jù)查詢,不僅僅體現(xiàn)在文件內(nèi)容的存儲結(jié)構(gòu)上,還有一點很重要,就是文件的命名上。試想一下,如果一個Datasource下有幾百萬個Segment文件,我們又如何快速找出我們所需要的文件呢?答案就是通過文件名稱快速索引查找。
Segment的命名包含四部分:數(shù)據(jù)源(Datasource)、時間間隔(包含開始時間和結(jié)束時間兩部分)、版本號和分區(qū)(Segment有分片的情況下才會有)。

test-datasource_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T16:00:00.000Z_1
數(shù)據(jù)源名稱_開始時間_結(jié)束時間_版本號_分區(qū)

分片號是從0開始,如果分區(qū)號為0,則可以省略:test-datasource_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T16:00:00.000Z
還需要注意如果一個時間間隔segment由多個分片組成,則在查詢該segment的時候,需要等到所有分片都被加載完成后,才能夠查詢(除非使用線性分片規(guī)范(linear shard spec),允許在未加載完成時查詢)。

字段 是否必須 描述
datasource segment所在的Datasource
開始時間 該Segment所存儲最早的數(shù)據(jù),時間格式是ISO 8601。開始時間和結(jié)束時間是通過segmentGranularity設置的時間間隔
結(jié)束時間 該segment所存儲最晚的數(shù)據(jù),時間格式是ISO 8601
版本號 因為Druid支持批量覆蓋操作,當批量攝入與之前相同數(shù)據(jù)源、相同時間間隔數(shù)據(jù)時,數(shù)據(jù)就會被覆蓋,這時候版本號就會被更新。Druid系統(tǒng)的其它部分感知到這個信號后,就會把就舊數(shù)據(jù)刪除,使用新版本的數(shù)據(jù)(這個切換很快)。版本號也是是用的ISO 8601時間戳,但是這個時間戳代表首次啟動的時間
分區(qū)號 segment如果采用分區(qū),才會有該標識

Segment物理存儲實例

下面我們以一個實例來看下Segment到底以什么形式存儲的,我們以本地導入方式將下面數(shù)據(jù)導入到Druid中。

{"time": "2018-11-01T00:47:29.913Z","city": "beijing","sex": "man","gmv": 20000}
{"time": "2018-11-01T00:47:33.004Z","city": "beijing","sex": "woman","gmv": 50000}
{"time": "2018-11-01T00:50:33.004Z","city": "shanghai","sex": "man","gmv": 10000}

我們以單機形式運行Druid,這樣Druid生成的Segment文件都在${DRUID_HOME}/var/druid/segments 目錄下。

image

segment通過datasource_beginTime_endTime_version_shard用于唯一標識,在實際存儲中是以目錄的形式表現(xiàn)的。

image

可以看到Segment中包含了Segment描述文件(descriptor.json)和壓縮后的索引數(shù)據(jù)文件(index.zip),我們主要看的也是index.zip這個文件,對其進行解壓縮。

image

首先看下factory.json這個文件,這個文件并不是segment具體存儲段數(shù)據(jù)的文件。因為Druid通過使用MMap(一種內(nèi)存映射文件的方式)的方式訪問Segment文件,通過查看這個文件內(nèi)容來看,貌似是用于MMap讀取文件所使用的(不太了解MMap)?

#factory.json文件內(nèi)容
{"type":"mMapSegmentFactory"}

Druid實際存儲Segment數(shù)據(jù)文件是:version.bin、meta.smoosh和xxxxx.smoosh這三個文件,下面分別看下這三個文件的內(nèi)容。
version.bin是一個存儲了4個字節(jié)的二進制文件,它是Segment內(nèi)部版本號(隨著Druid發(fā)展,Segment的格式也在發(fā)展),目前是V9,以Sublime打開該文件可以看到:

0000 0009 

meta.smoosh里面存儲了關(guān)于其它smoosh文件(xxxxx.smoosh)的元數(shù)據(jù),里面記錄了每一列對應文件和在文件的偏移量。除了列信息外,smoosh文件還包含了index.drd和metadata.drd,這部分是關(guān)于Segment的一些額外元數(shù)據(jù)信息。

#版本號,該文件所能存儲的最大值(2G),smooth文件數(shù)
v1,2147483647,1
# 列名,文件名,起始偏移量,結(jié)束偏移量
__time,0,0,154
city,0,306,577
gmv,0,154,306
index.drd,0,841,956
metadata.drd,0,956,1175
sex,0,577,841

再看00000.smoosh文件前,我們先想一下為什么這個文件被命名為這種樣式?因為Druid為了最小化減少打開文件的句柄數(shù),它會將一個Segment的所有列數(shù)據(jù)都存儲在一個smoosh文件中,也就是xxxxx.smoosh這個文件。但是由于Druid使用MMap來讀取Segment文件,而MMap需要保證每個文件大小不能超過2G(Java中的MMapByteBuffer限制),所以當一個smoosh文件大于2G時,Druid會將新數(shù)據(jù)寫入到下一個smoosh文件中。這也就是為什么這些文件命名是這樣的,這里也對應上了meta文件中為什么還要標識列所在的文件名。
通過meta.smoosh的偏移量也能看出,00000.smoosh文件中數(shù)據(jù)是按列進行存儲的,從上到下分別存儲的是時間列、指標列、維度列。對于每列主要包會含兩部分信息:ColumnDescriptor和binary數(shù)據(jù)。columnDescriptor是一個使用Jackson序列化的對象,它包含了該列的一些元數(shù)據(jù)信息,比如數(shù)據(jù)類型、是否是多值等。而binary則是根據(jù)不同數(shù)據(jù)類型進行壓縮存儲的二進制數(shù)據(jù)。

^@^@^@d{"valueType":"LONG","hasMultipleValues":false,"parts":[{"type":"long","byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^C^@^@ ^@^A^A^@^@^@^@"^@^@^@^A^@^@^@^Z^@^@^@^@¢yL?ìf^A^@^@<8c>X^H^@<80>?^Wàìf^A^@^@^@^@^@d{"valueType":"LONG","hasMultipleValues":false,"parts":[{"type":"long","byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^C^@^@ ^@^A^A^@^@^@^@ ^@^@^@^A^@^@^@^X^@^@^@^@1 N^@^A^@"P?^H^@<80>^P'^@^@^@^@^@^@^@^@^@<9a>{"valueType":"STRING","hasMultipleValues":false,"parts":[{"type":"stringDictionary","bitmapSerdeFactory":{"type":"concise"},"byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^@^A^A^@^@^@#^@^@^@^B^@^@^@^K^@^@^@^W^@^@^@^@beijing^@^@^@^@shanghai^B^A^@^@^@^C^@^A^@^@^A^A^@^@^@^@^P^@^@^@^A^@^@^@^H^@^@^@^@0^@^@^A^A^@^@^@^@^\^@^@^@^B^@^@^@^H^@^@^@^P^@^@^@^@<80>^@^@^C^@^@^@^@<80>^@^@^D^@^@^@<9a>{"valueType":"STRING","hasMultipleValues":false,"parts":[{"type":"stringDictionary","bitmapSerdeFactory":{"type":"concise"},"byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^@^A^A^@^@^@^\^@^@^@^B^@^@^@^G^@^@^@^P^@^@^@^@man^@^@^@^@woman^B^A^@^@^@^C^@^A^@^@^A^A^@^@^@^@^P^@^@^@^A^@^@^@^H^@^@^@^@0^@^A^@^A^@^@^@^@^\^@^@^@^B^@^@^@^H^@^@^@^P^@^@^@^@<80>^@^@^E^@^@^@^@<80>^@^@^B^A^@^@^@^@&^@^@^@^C^@^@^@^G^@^@^@^O^@^@^@^V^@^@^@^@gmv^@^@^@^@city^@^@^@^@sex^A^A^@^@^@^[^@^@^@^B^@^@^@^H^@^@^@^O^@^@^@^@city^@^@^@^@sex^@^@^Afì<91>D^@^@^@^Af??,^@^@^@^@^R{"type":"concise"}{"container":{},"aggregators":[{"type":"longSum","name":"gmv","fieldName":"gmv","expression":null}],"timestampSpec":{"column":"time","format":"auto","missingValue":null},"queryGranularity":{"type":"none"},"rollup":true}

smooth文件中的binary數(shù)據(jù)經(jīng)過LZ4或Bitmap壓縮,所以無法看到數(shù)據(jù)原始內(nèi)容。

在smooth文件最后還包含了兩部分數(shù)據(jù),分別是index.drd和metadata.drd。其中index.drd中包含了Segment中包含哪些度量、維度、時間范圍、以及使用哪種bitmap。metadata.drd中存儲了指標聚合函數(shù)、查詢粒度、時間戳配置等(上面內(nèi)容的最后部分)。
下圖是物理存儲結(jié)構(gòu)圖,存儲未壓縮和編碼的數(shù)據(jù)就是最右邊的內(nèi)容。

image

Segment創(chuàng)建

Segment都是在MiddleManager節(jié)點中創(chuàng)建的,并且處在MiddleManager中的Segment在狀態(tài)上都是可變的并且未提交的(提交到DeepStorage之后,數(shù)據(jù)就不可改變)。
Segment從在MiddleManager中創(chuàng)建到傳播到Historical中,會經(jīng)歷以下幾個步驟:

  1. MiddleManager中創(chuàng)建Segment文件,并將其發(fā)布到Deep Storage。
  2. Segment相關(guān)的元數(shù)據(jù)信息被存儲到MetaStore中。
  3. Coordinator進程根據(jù)MetaStore中得知Segment相關(guān)的元數(shù)據(jù)信息后,根據(jù)規(guī)則的設置分配給復合條件的Historical節(jié)點。
  4. Historical節(jié)點得到Coordinator指令后,自動從DeepStorage中拉取Segment數(shù)據(jù)文件,并通過Zookeeper向集群聲明負責提供該Segment數(shù)據(jù)相關(guān)的查詢服務。
  5. MiddleManager在得知Historical負責該Segment后,會丟棄該Segment文件,并向集群聲明不在負責該Segment相關(guān)的查詢。

如何配置分區(qū)

可以通過granularitySpec中的segmentGranularity設置segment的時間間隔(http://druid.io/docs/latest/ingestion/ingestion-spec.html#granularityspec)。為了保證Druid的查詢效率,每個Segment文件的大小建議在300MB~700MB之間。如果超過這個范圍,可以修改時間間隔或者使用分區(qū)來進行優(yōu)化(配置partitioningSpec中的targetPartitionSize,官方建議設置500萬行以上;http://druid.io/docs/latest/ingestion/hadoop.html#partitioning-specification)。

系統(tǒng)架構(gòu)詳解

我們知道Druid節(jié)點類型有五種:Overload、MiddleManager、Coordinator、Historical和Broker。

image

Overload和MiddleManager主要負責數(shù)據(jù)攝入(對于沒有發(fā)布的Segment,MiddleManager也提供查詢服務);Coordinator和Historical主要負責歷史數(shù)據(jù)的查詢;Broker節(jié)點主要負責接收Client查詢請求,拆分子查詢給MiddleManager和Historical節(jié)點,然后合并查詢結(jié)果返回給Client。其中Overload是MiddleManager的master節(jié)點,Coordinator是Historical的master節(jié)點。

索引服務

Druid提供一組支持索引服務(Indexing Service)的組件,也就是Overload和MiddleManager節(jié)點。索引服務是一種高可用的分布式服務,用于運行跟索引相關(guān)的任務,索引服務是數(shù)據(jù)攝入創(chuàng)建和銷毀Segment的主要方式(還有一種是采用實時節(jié)點的方式,但是現(xiàn)在已經(jīng)廢棄了)。索引服務支持以pull或push的方式攝入外部數(shù)據(jù)。
索引服務采用的是主從架構(gòu),Overload為主節(jié)點,MiddleManager是從節(jié)點。索引服務架構(gòu)圖如下圖所示:

image

索引服務由三部分組件組成:用于執(zhí)行任務的Peon(勞工)組件、用于管理Peon的MiddleManager組件和分配任務給MiddleManager的Overload組件。MiddleManager和Overload組件可以部署在相同節(jié)點也可以跨節(jié)點部署,但是Peon和MiddleManager是部署在同一個節(jié)點上的。
索引服務架構(gòu)和Yarn的架構(gòu)很像:

  • Overlaod節(jié)點相當于Yarn的ResourceManager,負責集群資源管理和任務分配。
  • MiddleManager節(jié)點相當于Yarn的NodeManager,負責接受任務和管理本節(jié)點的資源。
  • Peon節(jié)點相當于Yarn的Container,執(zhí)行節(jié)點上具體的任務。

Overload節(jié)點

Overload作為索引服務的主節(jié)點,對外負責接受索引任務,對內(nèi)負責將任務分解并下發(fā)給MiddleManager。Overload有兩種運行模式:

  • 本地模式(Local Mode):默認模式。本地模式下的Overload不僅負責任務協(xié)調(diào)工作,還會負責啟動一些peon來完成具體的任務。
  • 遠程模式(Remote Mode):該模式下,Overload和MiddleManager運行在不同的節(jié)點上,它僅負責任務的協(xié)調(diào)工作,不負責完成具體的任務。

Overload提供了一個UI客戶端,可以用于查看任務、運行任務和終止任務等。

http://<OVERLORD_IP>:<port>/console.html

Overload提供了RESETful的訪問形式,所以客戶端可以通過HTTP POST形式向請求節(jié)點提交任務。

http://<OVERLORD_IP>:<port>/druid/indexer/v1/task //提交任務
http://<OVERLORD_IP>:<port>/druid/indexer/v1/task/{task_id}/shutdown //殺死任務

MiddleManager節(jié)點

MiddleManager是執(zhí)行任務的工作節(jié)點,MiddleManager會將任務單獨發(fā)給每個單獨JVM運行的Peon(因為要把資源和日志進行隔離),每個Peon一次只能運行一個任務。

Peon節(jié)點

Peon在單個JVM中運行單個任務,MiddleManager負責為任務創(chuàng)建Peon。

Coordinator節(jié)點

Coordinator是Historical的mater節(jié)點,它主要負責管理和分發(fā)Segment。具體工作就是:告知Historical加載或刪除Segment、管理Segment副本以及負載Segment在Historical上的均衡。
Coordinator是定期運行的,并且運行間隔可以通過配置參數(shù)配置。每次Coordinator運行都會通過Zookeeper獲取當前集群狀態(tài),通過評估集群狀態(tài)來采取適當?shù)牟僮?比如均衡負載Segment)。Coordinator會連接數(shù)據(jù)庫(MetaStore),數(shù)據(jù)庫中存儲了Segment信息和規(guī)則(Rule)。Segment表中列出了需要加載到集群中的所有Segment,Coordinator每次運行都會從Segment表來拉取Segment列表并與當前集群的Segment對比,如果發(fā)現(xiàn)數(shù)據(jù)庫中不存在的Segment,但是在集群中還有,就會把它從集群刪掉;規(guī)則表定義了如何處理Segment,規(guī)則的作用就是我們可以通過配置一組規(guī)則,來操作集群加載Segment或刪除Segment。關(guān)于如何配置規(guī)則,可以查看:http://druid.io/docs/latest/operations/rule-configuration.html。

Historical節(jié)點加載Segment前,會進行容量排序,哪個Historical節(jié)點的Segment最少,則它就具有最高的加載權(quán)。Coordinator不會直接Historical節(jié)點通信,而是將Segment信息放到一個隊列中,Historical節(jié)點去隊列取Segment描述信息,并且加載該Segment到本節(jié)點。
Coordinator提供了一UI界面,用于顯示集群信息和規(guī)則配置:

http://<COORDINATOR_IP>:<COORDINATOR_PORT>

Historical節(jié)點

Historical節(jié)點負責管理歷史Segment,Historical節(jié)點通過Zookeeper監(jiān)聽指定的路徑來發(fā)現(xiàn)是否有新的Segment需要加載(Coordinator通過分配算法指定具體的Historical)。
上面通過Coordinator知道,當有新的Segment需要加載的時候,Coordinator會將其放到一個隊列中。當Historical節(jié)點收到有新的Segment時候,就會檢測本地cache和磁盤,查看是否有該Segment信息。如果沒有Historical節(jié)點會從Zookeeper中拉取該Segment相關(guān)的信息,然后進行下載。

image

Broker

Broker節(jié)點是負責轉(zhuǎn)發(fā)Client查詢請求的,Broker通過zookeeper能夠知道哪個Segment在哪些節(jié)點上,Broker會將查詢轉(zhuǎn)發(fā)給相應節(jié)點。所有節(jié)點返回數(shù)據(jù)后,Broker會將所有節(jié)點的數(shù)據(jù)進行合并,然后返回給Client。
Broker會有一個LRU(高速緩存失效策略),來緩存每Segment的結(jié)果。這個緩存可以是本地緩存,也可以借助外部緩存系統(tǒng)(比如memcached),第三方緩存可以在所有broker中共享Segment結(jié)果。當Borker接收到查詢請求后,會首先查看本地是否有對應的查詢數(shù)據(jù),對于不存在的Segment數(shù)據(jù),會將請求轉(zhuǎn)發(fā)給Historical節(jié)點。

轉(zhuǎn)載http://m.itdecent.cn/p/7a26d9153455

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們知道Druid能夠同時提供對大數(shù)據(jù)集的實時攝入和高效復雜查詢的性能,主要原因就是它獨到的架構(gòu)設計和基于Data...
    零度沸騰_yjz閱讀 21,845評論 3 17
  • Druid io總體設計 1.Druid模塊架構(gòu) 1.1 Druid簡介 最新版本的Druid采用了位圖索引、字典...
    小武大講堂閱讀 1,937評論 0 2
  • Druid.io(以下簡稱Druid)是面向海量數(shù)據(jù)的、用于實時查詢與分析的OLAP存儲系統(tǒng)。Druid的四大關(guān)鍵...
    大詩兄_zl閱讀 6,564評論 0 9
  • #refer1:http://www.cnblogs.com/xd502djj/p/6408979.html#re...
    liuzx32閱讀 2,027評論 0 1
  • 0. Overview 后面將寫幾篇文章介紹一下 OLAP 的大數(shù)據(jù)系統(tǒng)架構(gòu)。這里的 Druid 不是阿里巴巴的連...
    legendtkl閱讀 4,241評論 1 3

友情鏈接更多精彩內(nèi)容