Clickhouse是一個(gè)用于聯(lián)機(jī)分析處理(OLAP)的列式數(shù)據(jù)庫管理系統(tǒng)(columnar DBMS)园欣。
傳統(tǒng)數(shù)據(jù)庫在數(shù)據(jù)大小比較小迷扇,索引大小適合內(nèi)存,數(shù)據(jù)緩存命中率足夠高的情形下能正常提供服務(wù)疑苫。但殘酷的是阁簸,這種理想情形最終會隨著業(yè)務(wù)的增長走到盡頭肮帐,查詢會變得越來越慢塞弊。你可能通過增加更多的內(nèi)存,訂購更快的磁盤等等來解決問題(縱向擴(kuò)展)泪姨,但這只是拖延解決本質(zhì)問題游沿。如果你的需求是解決怎樣快速查詢出結(jié)果,那么ClickHouse也許可以解決你的問題肮砾。
應(yīng)用場景:
1.
絕大多數(shù)請求都是用于讀訪問的
2.
數(shù)據(jù)需要以大批次(大于1000行)進(jìn)行更新诀黍,而不是單行更新;或者根本沒有更新操作
3.
數(shù)據(jù)只是添加到數(shù)據(jù)庫仗处,沒有必要修改
4.
讀取數(shù)據(jù)時(shí)眯勾,會從數(shù)據(jù)庫中提取出大量的行枣宫,但只用到一小部分列
5.
表很“寬”,即表中包含大量的列
6.
查詢頻率相對較低(通常每臺服務(wù)器每秒查詢數(shù)百次或更少)
7.
對于簡單查詢吃环,允許大約50毫秒的延遲
8.
列的值是比較小的數(shù)值和短字符串(例如也颤,每個(gè)URL只有60個(gè)字節(jié))
9.
在處理單個(gè)查詢時(shí)需要高吞吐量(每臺服務(wù)器每秒高達(dá)數(shù)十億行)
10.
不需要事務(wù)
11.
數(shù)據(jù)一致性要求較低
12.
每次查詢中只會查詢一個(gè)大表。除了一個(gè)大表郁轻,其余都是小表
13.
查詢結(jié)果顯著小于數(shù)據(jù)源翅娶。即數(shù)據(jù)有過濾或聚合。返回結(jié)果不超過單個(gè)服務(wù)器內(nèi)存大小
相應(yīng)地好唯,使用ClickHouse也有其本身的限制:
1.
不支持真正的刪除/更新支持 不支持事務(wù)(期待后續(xù)版本支持)
2.
不支持二級索引
3.
有限的SQL支持竭沫,join實(shí)現(xiàn)與眾不同
4.
不支持窗口功能
5.
元數(shù)據(jù)管理需要人工干預(yù)維護(hù)
常用SQL語法
-- 列出數(shù)據(jù)庫列表
show databases;
-- 列出數(shù)據(jù)庫中表列表
show tables;
-- 創(chuàng)建數(shù)據(jù)庫
create database test;
-- 刪除一個(gè)表
drop table if exists test.t1;
-- 創(chuàng)建第一個(gè)表
create /*temporary*/ table /*if not exists*/ test.m1 (
id UInt16
,name String
) ENGINE = Memory
;
-- 插入測試數(shù)據(jù)
insert into test.m1 (id, name) values (1, 'abc'), (2, 'bbbb');
-- 查詢
select * from test.m1;
默認(rèn)值
默認(rèn)值 的處理方面, ClickHouse 中骑篙,默認(rèn)值總是有的蜕提,如果沒有顯示式指定的話,會按字段類型處理:
數(shù)字類型靶端, 0
字符串谎势,空字符串
數(shù)組,空數(shù)組
日期杨名, 0000-00-00
時(shí)間它浅, 0000-00-00 00:00:00
注:NULLs 是不支持的
數(shù)據(jù)類型
1.
整型:UInt8,UInt16,UInt32,UInt64,Int8,Int16,Int32,Int64
范圍U開頭-2N/2~2N-1;非U開頭0~2^N-1
2.
枚舉類型:Enum8,Enum16
Enum('hello'=1,'test'=-1),Enum是有符號的整型映射的,因此負(fù)數(shù)也是可以的
3.
字符串型:FixedString(N),String
N是最大字節(jié)數(shù)镣煮,不是字符長度,如果是UTF8字符串鄙麦,那么就會占3個(gè)字節(jié)典唇,GBK會占2字節(jié);String可以用來替換VARCHAR,BLOB,CLOB等數(shù)據(jù)類型
4.
時(shí)間類型:Date
5.
數(shù)組類型:Array(T)
T是一個(gè)基本類型,包括arry在內(nèi)胯府,官方不建議使用多維數(shù)組
6.
元組:Tuple
7.
結(jié)構(gòu):Nested(name1 Type1,name2 Type2,...)
類似一種map的結(jié)
物化列
指定 MATERIALIZED 表達(dá)式介衔,即將一個(gè)列作為物化列
處理了,這意味著這個(gè)列的值不能從insert
語句獲取骂因,只能是自己計(jì)算出來的炎咖。同時(shí),
物化列也不會出現(xiàn)在 select *
的結(jié)果中:
drop table if exists test.m2;
create table test.m2 (
a MATERIALIZED (b+1)
,b UInt16
) ENGINE = Memory;
insert into test.m2 (b) values (1);
select * from test.m2;
select a, b from test.m2;
表達(dá)式列
ALIAS 表達(dá)式列某方面跟物化列相同寒波,就是它的值不能從 insert 語句獲取乘盼。不同的是, 物化列 是會真正保存數(shù)據(jù)(這樣查詢時(shí)不需要再計(jì)算)俄烁,
而表達(dá)式列不會保存數(shù)據(jù)(這樣查詢時(shí)總是需要計(jì)算)绸栅,只是在查詢時(shí)返回表達(dá)式的結(jié)果。
create table test.m3 (a ALIAS (b+1), b UInt16) ENGINE = Memory;
insert into test.m3(b) values (1);
select * from test.m3;
select a, b from test.m3;
引擎/engine
引擎是clickhouse設(shè)計(jì)的精華部分
TinyLog
最簡單的一種引擎页屠,每一列保存為一個(gè)文件粹胯,里面的內(nèi)容是壓縮過的蓖柔,不支持索引
這種引擎沒有并發(fā)控制,所以风纠,當(dāng)你需要在讀况鸣,又在寫時(shí),讀會出錯(cuò)竹观。并發(fā)寫镐捧,內(nèi)容都會壞掉。
應(yīng)用場景:
a. 基本上就是那種只寫一次
b. 然后就是只讀的場景栈幸。
c. 不適用于處理量大的數(shù)據(jù)愤估,官方推薦,使用這種引擎的表最多 100 萬行的數(shù)據(jù)
drop table if exists test.tinylog;
create table test.tinylog (a UInt16, b UInt16) ENGINE = TinyLog;
insert into test.tinylog(a,b) values (7,13);
此時(shí)/var/lib/clickhouse/data/test/tinylog
保存數(shù)據(jù)的目錄結(jié)構(gòu):
├── a.bin
├── b.bin
└── sizes.json
a.bin 和 b.bin 是壓縮過的對應(yīng)的列的數(shù)據(jù)速址, sizes.json 中記錄了每個(gè) *.bin 文件的大小
Log
這種引擎跟 TinyLog 基本一致
它的改進(jìn)點(diǎn)玩焰,是加了一個(gè) __marks.mrk 文件,里面記錄了每個(gè)數(shù)據(jù)塊的偏移
這樣做的一個(gè)用處芍锚,就是可以準(zhǔn)確地切分讀的范圍昔园,從而使用并發(fā)讀取成為可能
但是,它是不能支持并發(fā)寫的并炮,一個(gè)寫操作會阻塞其它讀寫操作
Log 不支持索引默刚,同時(shí)因?yàn)橛幸粋€(gè) __marks.mrk 的冗余數(shù)據(jù),所以在寫入數(shù)據(jù)時(shí)逃魄,一旦出現(xiàn)問題荤西,這個(gè)表就廢了
應(yīng)用場景:
同 TinyLog 差不多,它適用的場景也是那種寫一次之后伍俘,后面就是只讀的場景邪锌,臨時(shí)數(shù)據(jù)用它保存也可以
drop table if exists test.log;
create table test.log (a UInt16, b UInt16) ENGINE = Log;
insert into test.log(a,b) values (7,13);
此時(shí)/var/lib/clickhouse/data/test/log
保存數(shù)據(jù)的目錄結(jié)構(gòu):
├── __marks.mrk
├── a.bin
├── b.bin
└── sizes.json
Memory
內(nèi)存引擎,數(shù)據(jù)以未壓縮的原始形式直接保存在內(nèi)存當(dāng)中癌瘾,服務(wù)器重啟數(shù)據(jù)就會消失
可以并行讀觅丰,讀寫互斥鎖的時(shí)間也非常短
不支持索引,簡單查詢下有非常非常高的性能表現(xiàn)
應(yīng)用場景:
a. 進(jìn)行測試
b. 在需要非常高的性能妨退,同時(shí)數(shù)據(jù)量又不太大(上限大概 1 億行)的場景
Merge
一個(gè)工具引擎妇萄,本身不保存數(shù)據(jù),只用于把指定庫中的指定多個(gè)表鏈在一起咬荷。
這樣冠句,讀取操作可以并發(fā)執(zhí)行,同時(shí)也可以利用原表的索引幸乒,但是轩端,此引擎不支持寫操作
指定引擎的同時(shí),需要指定要鏈接的庫及表逝变,庫名可以使用一個(gè)表達(dá)式基茵,表名可以使用正則表達(dá)式指定
create table test.tinylog1 (id UInt16, name String) ENGINE=TinyLog;
create table test.tinylog2 (id UInt16, name String) ENGINE=TinyLog;
create table test.tinylog3 (id UInt16, name String) ENGINE=TinyLog;
insert into test.tinylog1(id, name) values (1, 'tinylog1');
insert into test.tinylog2(id, name) values (2, 'tinylog2');
insert into test.tinylog3(id, name) values (3, 'tinylog3');
use test;
create table test.merge (id UInt16, name String) ENGINE=Merge(currentDatabase(), '^tinylog[0-9]+');
select _table,* from test.merge order by id desc
┌─_table───┬─id─┬─name─────┐
│ tinylog3 │ 3 │ tinylog3 │
│ tinylog2 │ 2 │ tinylog2 │
│ tinylog1 │ 1 │ tinylog1 │
└──────────┴────┴──────────┘
注:
_table 這個(gè)列奋构,是因?yàn)槭褂昧?Merge 多出來的一個(gè)的一個(gè)虛擬列
a. 它表示原始數(shù)據(jù)的來源表,它不會出現(xiàn)在
show table
的結(jié)果當(dāng)中
b.select *
不會包含它
Distributed
與 Merge 類似拱层, Distributed 也是通過一個(gè)邏輯表弥臼,去訪問各個(gè)物理表,設(shè)置引擎時(shí)的樣子是:
Distributed(remote_group, database, table [, sharding_key])
其中:
remote_group
/etc/clickhouse-server/config.xml中remote_servers參數(shù)
database
是各服務(wù)器中的庫名
table
是表名
sharding_key
是一個(gè)尋址表達(dá)式根灯,可以是一個(gè)列名径缅,也可以是像 rand() 之類的函數(shù)調(diào)用,它與 remote_servers 中的 weight 共同作用烙肺,決定在 寫 時(shí)往哪個(gè) shard 寫
配置文件中的 remote_servers
<remote_servers>
<log>
<shard>
<weight>1</weight>
<internal_replication>false</internal_replication>
<replica>
<host>172.17.0.3</host>
<port>9000</port>
</replica>
</shard>
<shard>
<weight>2</weight>
<internal_replication>false</internal_replication>
<replica>
<host>172.17.0.4</host>
<port>9000</port>
</replica>
</shard>
</log>
</remote_servers>
log
是某個(gè) shard 組的名字纳猪,就是上面的 remote_group 的值
shard
是固定標(biāo)簽
weight
是權(quán)重,前面說的 sharding_key 與這個(gè)有關(guān)桃笙。
簡單來說氏堤,上面的配置,理論上來看:
第一個(gè) shard “被選中”的概率是 1 / (1 + 2) 搏明,第二個(gè)是 2 / (1 + 2) 鼠锈,這很容易理解。但是星著, sharding_key 的工作情況购笆,是按實(shí)際數(shù)字的“命中區(qū)間”算的,即第一個(gè)的區(qū)間是 [0, 1) 的周期虚循,第二個(gè)區(qū)間是 [1, 1+2) 的周期同欠。比如把 sharding_key 設(shè)置成 id ,當(dāng) id=0 或 id=3 時(shí)横缔,一定是寫入到第一個(gè) shard 中铺遂,如果把 sharding_key 設(shè)置成 rand() ,那系統(tǒng)會對應(yīng)地自己作一般化轉(zhuǎn)換吧剪廉,這種時(shí)候就是一種概率場景了。
internal_replication
是定義針對多個(gè) replica 時(shí)的寫入行為的炕檩。
如果為 false 斗蒋,則會往所有的 replica 中寫入數(shù)據(jù),但是并不保證數(shù)據(jù)寫入的一致性笛质,所以這種情況時(shí)間一長泉沾,各 replica 的數(shù)據(jù)很可能出現(xiàn)差異。如果為 true 妇押,則只會往第一個(gè)可寫的 replica 中寫入數(shù)據(jù)(剩下的事“物理表”自己處理)跷究。
replica
就是定義各個(gè)冗余副本的,選項(xiàng)有 host 敲霍, port 俊马, user 丁存, password 等
看一個(gè)實(shí)際的例子,我們先在兩臺機(jī)器上創(chuàng)建好物理表并插入一些測試數(shù)據(jù):
create table test.tinylog_d1(id UInt16, name String) ENGINE=TinyLog;
insert into test.tinylog_d1(id, name) values (1, 'Distributed record 1');
insert into test.tinylog_d1(id, name) values (2, 'Distributed record 2');
在其中一臺創(chuàng)建邏輯表:
create table test.tinylog_d (id UInt16, name String) ENGINE=Distributed(log, test,tinylog_d1 , id);
-- 插入數(shù)據(jù)到邏輯表柴我,觀察數(shù)據(jù)分發(fā)情況
insert into test.tinylog_d(id, name) values (0, 'main');
insert into test.tinylog_d(id, name) values (1, 'main');
insert into test.tinylog_d(id, name) values (2, 'main');
select name,sum(id),count(id) from test.tinylog_d group by name;
注:
邏輯表中的寫入操作是異步的解寝,會先緩存在本機(jī)的文件系統(tǒng)上,并且艘儒,對于物理表的不可訪問狀態(tài)聋伦,并沒有嚴(yán)格控制,所以寫入失敗丟數(shù)據(jù)的情況是可能發(fā)生的
Null
空引擎界睁,寫入的任何數(shù)據(jù)都會被忽略觉增,讀取的結(jié)果一定是空。
但是注意翻斟,雖然數(shù)據(jù)本身不會被存儲逾礁,但是結(jié)構(gòu)上的和數(shù)據(jù)格式上的約束還是跟普通表一樣是存在的,同時(shí)杨赤,你也可以在這個(gè)引擎上創(chuàng)建視圖
Buffer
1.
Buffer 引擎敞斋,像是Memory 存儲的一個(gè)上層應(yīng)用似的(磁盤上也是沒有相應(yīng)目錄的)
2.
它的行為是一個(gè)緩沖區(qū),寫入的數(shù)據(jù)先被放在緩沖區(qū)疾牲,達(dá)到一個(gè)閾值后植捎,這些數(shù)據(jù)會自動被寫到指定的另一個(gè)表中
3.
和Memory 一樣,有很多的限制阳柔,比如沒有索引
4.
Buffer 是接在其它表前面的一層焰枢,對它的讀操作,也會自動應(yīng)用到后面表舌剂,但是因?yàn)榍懊嬲f到的限制的原因济锄,一般我們讀數(shù)據(jù),就直接從源表讀就好了霍转,緩沖區(qū)的這點(diǎn)數(shù)據(jù)延遲荐绝,只要配置得當(dāng),影響不大的
5.
Buffer 后面也可以不接任何表避消,這樣的話低滩,當(dāng)數(shù)據(jù)達(dá)到閾值,就會被丟棄掉
一些特點(diǎn):
- 如果一次寫入的數(shù)據(jù)太大或太多岩喷,超過了 max 條件恕沫,則會直接寫入源表。
- 刪源表或改源表的時(shí)候纱意,建議 Buffer 表刪了重建婶溯。
- “友好重啟”時(shí), Buffer 數(shù)據(jù)會先落到源表,“暴力重啟”迄委, Buffer 表中的數(shù)據(jù)會丟失褐筛。
- 即使使用了 Buffer ,多次的小數(shù)據(jù)寫入跑筝,對比一次大數(shù)據(jù)寫入死讹,也 慢得多 (幾千行與百萬行的差距)
-- 創(chuàng)建源表
create table test.mergetree (sdt Date, id UInt16, name String, point UInt16) ENGINE=MergeTree(sdt, (id, name), 10);
-- 創(chuàng)建 Buffer表
-- Buffer(database, table, num_layers, min_time, max_time, min_rows, max_rows, min_bytes, max_bytes)
create table test.mergetree_buffer as test.mergetree ENGINE=Buffer(test, mergetree, 16, 3, 20, 2, 10, 1, 10000);
insert into test.mergetree (sdt, id, name, point) values ('2017-07-10', 1, 'a', 20);
insert into test.mergetree_buffer (sdt, id, name, point) values ('2017-07-10', 1, 'b', 10);
select * from test.mergetree;
select '------';
select * from test.mergetree_buffer;
database
數(shù)據(jù)庫
table
源表,這里除了字符串常量曲梗,也可以使用變量的赞警。
num_layers
是類似“分區(qū)”的概念,每個(gè)分區(qū)的后面的 min / max 是獨(dú)立計(jì)算的虏两,官方推薦的值是 16 愧旦。
min / max
這組配置薦,就是設(shè)置閾值的定罢,分別是 時(shí)間(秒)笤虫,行數(shù),空間(字節(jié))祖凫。
閾值的規(guī)則:
是“所有的 min 條件都滿足琼蚯, 或 至少一個(gè) max 條件滿足”。
如果按上面我們的建表來說惠况,所有的 min 條件就是:過了 3秒遭庶,2條數(shù)據(jù),1 Byte稠屠。一個(gè) max 條件是:20秒峦睡,或 10 條數(shù)據(jù),或有 10K
Set
Set 這個(gè)引擎有點(diǎn)特殊权埠,因?yàn)樗挥迷?IN 操作符右側(cè)榨了,你不能對它 select
create table test.set(id UInt16, name String) ENGINE=Set;
insert into test.set(id, name) values (1, 'hello');
-- select 1 where (1, 'hello') in test.set; -- 默認(rèn)UInt8 需要手動進(jìn)行類型轉(zhuǎn)換
select 1 where (toUInt16(1), 'hello') in test.set;
注:
Set 引擎表,是全內(nèi)存運(yùn)行的攘蔽,但是相關(guān)數(shù)據(jù)會落到磁盤上保存龙屉,啟動時(shí)會加載到內(nèi)存中。所以满俗,意外中斷或暴力重啟转捕,是可能產(chǎn)生數(shù)據(jù)丟失問題的
Join
TODO
MergeTree
這個(gè)引擎是 ClickHouse 的重頭戲
,它支持一個(gè)日期和一組主鍵的兩層式索引
漫雷,還可以實(shí)時(shí)更新數(shù)據(jù)
瓜富。同時(shí)鳍咱,索引的粒度可以自定義降盹,外加直接支持采樣功能
MergeTree(EventDate, (CounterID, EventDate), 8192)
MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID)), 8192)
EventDate
一個(gè)日期的列名
intHash32(UserID)
采樣表達(dá)式
(CounterID, EventDate)
主鍵組(里面除了列名,也支持表達(dá)式),也可以是一個(gè)表達(dá)式
8192
主鍵索引的粒度
drop table if exists test.mergetree1;
create table test.mergetree1 (sdt Date, id UInt16, name String, cnt UInt16) ENGINE=MergeTree(sdt, (id, name), 10);
-- 日期的格式蓄坏,好像必須是 yyyy-mm-dd
insert into test.mergetree1(sdt, id, name, cnt) values ('2018-06-01', 1, 'aaa', 10);
insert into test.mergetree1(sdt, id, name, cnt) values ('2018-06-02', 4, 'bbb', 10);
insert into test.mergetree1(sdt, id, name, cnt) values ('2018-06-03', 5, 'ccc', 11);
此時(shí)/var/lib/clickhouse/data/test/mergetree1
的目錄結(jié)構(gòu):
├── 20180601_20180601_1_1_0
│ ├── checksums.txt
│ ├── columns.txt
│ ├── id.bin
│ ├── id.mrk
│ ├── name.bin
│ ├── name.mrk
│ ├── cnt.bin
│ ├── cnt.mrk
│ ├── cnt.idx
│ ├── primary.idx
│ ├── sdt.bin
│ └── sdt.mrk -- 保存一下塊偏移量
├── 20180602_20180602_2_2_0
│ └── ...
├── 20180603_20180603_3_3_0
│ └── ...
├── format_version.txt
└── detached
ReplacingMergeTree
1
.在 MergeTree 的基礎(chǔ)上价捧,添加了“處理重復(fù)數(shù)據(jù)”的功能=>實(shí)時(shí)數(shù)據(jù)場景
2
.相比 MergeTree ,ReplacingMergeTree 在最后加一個(gè)"版本列",它跟時(shí)間列配合一起,用以區(qū)分哪條數(shù)據(jù)是"新的"涡戳,并把舊的丟掉(這個(gè)過程是在 merge 時(shí)處理结蟋,不是數(shù)據(jù)寫入時(shí)就處理了的,平時(shí)重復(fù)的數(shù)據(jù)還是保存著的渔彰,并且查也是跟平常一樣會查出來的)
3
.主鍵列組用于區(qū)分重復(fù)的行
-- 版本列 允許的類型是嵌屎, UInt 一族的整數(shù),或 Date 或 DateTime
create table test.replacingmergetree (sdt Date, id UInt16, name String, cnt UInt16) ENGINE=ReplacingMergeTree(sdt, (name), 10, cnt);
insert into test.replacingmergetree (sdt, id, name, cnt) values ('2018-06-10', 1, 'a', 20);
insert into test.replacingmergetree (sdt, id, name, cnt) values ('2018-06-10', 1, 'a', 30);
insert into test.replacingmergetree (sdt, id, name, cnt) values ('2018-06-11', 1, 'a', 20);
insert into test.replacingmergetree (sdt, id, name, cnt) values ('2018-06-11', 1, 'a', 30);
insert into test.replacingmergetree (sdt, id, name, cnt) values ('2018-06-11', 1, 'a', 10);
select * from test.replacingmergetree;
-- 如果記錄未執(zhí)行merge恍涂,可以手動觸發(fā)一下 merge 行為
optimize table test.replacingmergetree;
┌────────sdt─┬─id─┬─name─┬─cnt─┐
│ 2018-06-11 │ 1 │ a │ 30 │
└────────────┴────┴──────┴─────┘
SummingMergeTree
1
.SummingMergeTree 就是在 merge 階段把數(shù)據(jù)sum求和
2
.sum求和的列可以指定宝惰,不可加的未指定列,會取一個(gè)最先出現(xiàn)的值
create table test.summingmergetree (sdt Date, name String, a UInt16, b UInt16) ENGINE=SummingMergeTree(sdt, (sdt, name), 8192, (a));
insert into test.summingmergetree (sdt, name, a, b) values ('2018-06-10', 'a', 1, 20);
insert into test.summingmergetree (sdt, name, a, b) values ('2018-06-10', 'b', 2, 11);
insert into test.summingmergetree (sdt, name, a, b) values ('2018-06-11', 'b', 3, 18);
insert into test.summingmergetree (sdt, name, a, b) values ('2018-06-11', 'b', 3, 82);
insert into test.summingmergetree (sdt, name, a, b) values ('2018-06-11', 'a', 3, 11);
insert into test.summingmergetree (sdt, name, a, b) values ('2018-06-12', 'c', 1, 35);
-- 手動觸發(fā)一下 merge 行為
optimize table test.summingmergetree;
select * from test.summingmergetree;
┌────────sdt─┬─name─┬─a─┬──b─┐
│ 2018-06-10 │ a │ 1 │ 20 │
│ 2018-06-10 │ b │ 2 │ 11 │
│ 2018-06-11 │ a │ 3 │ 11 │
│ 2018-06-11 │ b │ 6 │ 18 │
│ 2018-06-12 │ c │ 1 │ 35 │
└────────────┴──────┴───┴────┘
注:
可加列不能是主鍵中的列再沧,并且如果某行數(shù)據(jù)可加列都是 null 尼夺,則這行會被刪除
AggregatingMergeTree
AggregatingMergeTree 是在 MergeTree 基礎(chǔ)之上,針對聚合函數(shù)結(jié)果炒瘸,作增量計(jì)算優(yōu)化的一個(gè)設(shè)計(jì)淤堵,它會在 merge 時(shí),針對主鍵預(yù)處理聚合的數(shù)據(jù)
應(yīng)用于AggregatingMergeTree 上的聚合函數(shù)除了普通的 sum, uniq等顷扩,還有 sumState , uniqState 拐邪,及 sumMerge , uniqMerge 這兩組
1.
聚合數(shù)據(jù)的預(yù)計(jì)算
是一種“空間換時(shí)間”的權(quán)衡屎即,并且是以減少維度為代價(jià)的
dim1 | dim2 | dim3 | measure1 |
---|---|---|---|
aaaa | a | 1 | 1 |
aaaa | b | 2 | 1 |
bbbb | b | 3 | 1 |
cccc | b | 2 | 1 |
cccc | c | 1 | 1 |
dddd | c | 2 | 1 |
dddd | a | 1 | 1 |
假設(shè)原始有三個(gè)維度庙睡,一個(gè)需要 count 的指標(biāo)
dim1 | dim2 | dim3 | measure1 |
---|---|---|---|
aaaa | a | 1 | 1 |
aaaa | b | 2 | 1 |
bbbb | b | 3 | 1 |
cccc | b | 2 | 1 |
cccc | c | 1 | 1 |
dddd | c | 2 | 1 |
dddd | a | 1 | 1 |
通過減少一個(gè)維度的方式,來以 count 函數(shù)聚合一次 M
dim2 | dim3 | count(measure1) |
---|---|---|
a | 1 | 3 |
b | 2 | 2 |
b | 3 | 1 |
c | 1 | 1 |
c | 2 | 1 |
2.
聚合數(shù)據(jù)的增量計(jì)算
對于 AggregatingMergeTree 引擎的表技俐,不能使用普通的 INSERT 去添加數(shù)據(jù)乘陪,可以用:
a.
INSERT SELECT 來插入數(shù)據(jù)
b.
更常用的,是可以創(chuàng)建一個(gè)物化視圖
drop table if exists test.aggregatingmergetree;
create table test.aggregatingmergetree(
sdt Date
, dim1 String
, dim2 String
, dim3 String
, measure1 UInt64
) ENGINE=MergeTree(sdt, (sdt, dim1, dim2, dim3), 8192);
-- 創(chuàng)建一個(gè)物化視圖雕擂,使用 AggregatingMergeTree
drop table if exists test.aggregatingmergetree_view;
create materialized view test.aggregatingmergetree_view
ENGINE = AggregatingMergeTree(sdt,(dim2, dim3), 8192)
as
select sdt,dim2, dim3, uniqState(dim1) as uv
from test.aggregatingmergetree
group by sdt,dim2, dim3;
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'aaaa', 'a', '10', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'aaaa', 'a', '10', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'aaaa', 'b', '20', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'bbbb', 'b', '30', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'cccc', 'b', '20', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'cccc', 'c', '10', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'dddd', 'c', '20', 1);
insert into test.aggregatingmergetree (sdt, dim1, dim2, dim3, measure1) values ('2018-06-10', 'dddd', 'a', '10', 1);
-- 按 dim2 和 dim3 聚合 count(measure1)
select dim2, dim3, count(measure1) from test.aggregatingmergetree group by dim2, dim3;
-- 按 dim2 聚合 UV
select dim2, uniq(dim1) from test.aggregatingmergetree group by dim2;
-- 手動觸發(fā)merge
OPTIMIZE TABLE test.aggregatingmergetree_view;
select * from test.aggregatingmergetree_view;
-- 查 dim2 的 uv
select dim2, uniqMerge(uv) from test.aggregatingmergetree_view group by dim2 order by dim2;
CollapsingMergeTree
是專門為 OLAP 場景下啡邑,一種“變通”存數(shù)做法而設(shè)計(jì)的,在數(shù)據(jù)是不能改井赌,更不能刪的前提下谤逼,通過“運(yùn)算”的方式,去抹掉舊數(shù)據(jù)的影響仇穗,把舊數(shù)據(jù)“減”去即可流部,從而解決"最終狀態(tài)"類的問題,比如 當(dāng)前有多少人在線纹坐?
“以加代刪”的增量存儲方式枝冀,帶來了聚合計(jì)算方便的好處,代價(jià)卻是存儲空間的翻倍,并且果漾,對于只關(guān)心最新狀態(tài)的場景球切,中間數(shù)據(jù)都是無用的
CollapsingMergeTree 在創(chuàng)建時(shí)與 MergeTree 基本一樣,除了最后多了一個(gè)參數(shù)绒障,需要指定 Sign 位(必須是 Int8 類型)
create table test.collapsingmergetree(sign Int8, sdt Date, name String, cnt UInt16) ENGINE=CollapsingMergeTree(sdt, (sdt, name), 8192, sign);