clickhouse 基础知识
轉(zhuǎn)自:https://www.jianshu.com/p/a5bf490247ea
?
Clickhouse是一個(gè)用于聯(lián)機(jī)分析處理(OLAP)的列式數(shù)據(jù)庫(kù)管理系統(tǒng)(columnar DBMS)。
傳統(tǒng)數(shù)據(jù)庫(kù)在數(shù)據(jù)大小比較小,索引大小適合內(nèi)存,數(shù)據(jù)緩存命中率足夠高的情形下能正常提供服務(wù)。但殘酷的是,這種理想情形最終會(huì)隨著業(yè)務(wù)的增長(zhǎng)走到盡頭,查詢會(huì)變得越來(lái)越慢。你可能通過(guò)增加更多的內(nèi)存,訂購(gòu)更快的磁盤(pán)等等來(lái)解決問(wèn)題(縱向擴(kuò)展),但這只是拖延解決本質(zhì)問(wèn)題。如果你的需求是解決怎樣快速查詢出結(jié)果,那么ClickHouse也許可以解決你的問(wèn)題。
應(yīng)用場(chǎng)景:
1.絕大多數(shù)請(qǐng)求都是用于讀訪問(wèn)的2.數(shù)據(jù)需要以大批次(大于1000行)進(jìn)行更新,而不是單行更新;或者根本沒(méi)有更新操作3.數(shù)據(jù)只是添加到數(shù)據(jù)庫(kù),沒(méi)有必要修改4.讀取數(shù)據(jù)時(shí),會(huì)從數(shù)據(jù)庫(kù)中提取出大量的行,但只用到一小部分列5.表很“寬”,即表中包含大量的列6.查詢頻率相對(duì)較低(通常每臺(tái)服務(wù)器每秒查詢數(shù)百次或更少)7.對(duì)于簡(jiǎn)單查詢,允許大約50毫秒的延遲8.列的值是比較小的數(shù)值和短字符串(例如,每個(gè)URL只有60個(gè)字節(jié))9.在處理單個(gè)查詢時(shí)需要高吞吐量(每臺(tái)服務(wù)器每秒高達(dá)數(shù)十億行)10.不需要事務(wù)11.數(shù)據(jù)一致性要求較低12.每次查詢中只會(huì)查詢一個(gè)大表。除了一個(gè)大表,其余都是小表13.查詢結(jié)果顯著小于數(shù)據(jù)源。即數(shù)據(jù)有過(guò)濾或聚合。返回結(jié)果不超過(guò)單個(gè)服務(wù)器內(nèi)存大小
相應(yīng)地,使用ClickHouse也有其本身的限制:
1.不支持真正的刪除/更新支持 不支持事務(wù)(期待后續(xù)版本支持)2.不支持二級(jí)索引3.有限的SQL支持,join實(shí)現(xiàn)與眾不同4.不支持窗口功能5.元數(shù)據(jù)管理需要人工干預(yù)維護(hù)
?
常用SQL語(yǔ)法
-- 列出數(shù)據(jù)庫(kù)列表
show databases;-- 列出數(shù)據(jù)庫(kù)中表列表
show tables;-- 創(chuàng)建數(shù)據(jù)庫(kù)
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
;
-- 插入測(cè)試數(shù)據(jù)
insert into test.m1 (id, name) values (1, 'abc'), (2, 'bbbb');-- 查詢
select * from test.m1;
?
默認(rèn)值
默認(rèn)值 的處理方面, ClickHouse 中,默認(rèn)值總是有的,如果沒(méi)有顯示式指定的話,會(huì)按字段類型處理:
數(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開(kāi)頭-2N/2~2N-1;非U開(kāi)頭0~2^N-12.枚舉類型:Enum8,Enum16
Enum('hello'=1,'test'=-1),Enum是有符號(hào)的整型映射的,因此負(fù)數(shù)也是可以的3.字符串型:FixedString(N),String
N是最大字節(jié)數(shù),不是字符長(zhǎng)度,如果是UTF8字符串,那么就會(huì)占3個(gè)字節(jié),GBK會(huì)占2字節(jié);String可以用來(lái)替換VARCHAR,BLOB,CLOB等數(shù)據(jù)類型4.時(shí)間類型:Date5.數(shù)組類型:Array(T)
T是一個(gè)基本類型,包括arry在內(nèi),官方不建議使用多維數(shù)組6.元組:Tuple7.結(jié)構(gòu):Nested(name1 Type1,name2 Type2,...)
類似一種map的結(jié)
?
物化列
指定 MATERIALIZED 表達(dá)式,即將一個(gè)列作為物化列處理了,這意味著這個(gè)列的值不能從insert 語(yǔ)句獲取,只能是自己計(jì)算出來(lái)的。同時(shí),
物化列也不會(huì)出現(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 語(yǔ)句獲取。不同的是, 物化列 是會(huì)真正保存數(shù)據(jù)(這樣查詢時(shí)不需要再計(jì)算),
而表達(dá)式列不會(huì)保存數(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
最簡(jiǎn)單的一種引擎,每一列保存為一個(gè)文件,里面的內(nèi)容是壓縮過(guò)的,不支持索引
這種引擎沒(méi)有并發(fā)控制,所以,當(dāng)你需要在讀,又在寫(xiě)時(shí),讀會(huì)出錯(cuò)。并發(fā)寫(xiě),內(nèi)容都會(huì)壞掉。
應(yīng)用場(chǎng)景:
a. 基本上就是那種只寫(xiě)一次
b. 然后就是只讀的場(chǎng)景。
c. 不適用于處理量大的數(shù)據(jù),官方推薦,使用這種引擎的表最多 100 萬(wàn)行的數(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 是壓縮過(guò)的對(duì)應(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ā)寫(xiě)的,一個(gè)寫(xiě)操作會(huì)阻塞其它讀寫(xiě)操作
Log 不支持索引,同時(shí)因?yàn)橛幸粋€(gè) __marks.mrk 的冗余數(shù)據(jù),所以在寫(xiě)入數(shù)據(jù)時(shí),一旦出現(xiàn)問(wèn)題,這個(gè)表就廢了
應(yīng)用場(chǎng)景:
同 TinyLog 差不多,它適用的場(chǎng)景也是那種寫(xiě)一次之后,后面就是只讀的場(chǎng)景,臨時(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ù)就會(huì)消失
可以并行讀,讀寫(xiě)互斥鎖的時(shí)間也非常短
不支持索引,簡(jiǎn)單查詢下有非常非常高的性能表現(xiàn)
應(yīng)用場(chǎng)景:
a. 進(jìn)行測(cè)試
b. 在需要非常高的性能,同時(shí)數(shù)據(jù)量又不太大(上限大概 1 億行)的場(chǎng)景
?
Merge
一個(gè)工具引擎,本身不保存數(shù)據(jù),只用于把指定庫(kù)中的指定多個(gè)表鏈在一起。
這樣,讀取操作可以并發(fā)執(zhí)行,同時(shí)也可以利用原表的索引,但是,此引擎不支持寫(xiě)操作
指定引擎的同時(shí),需要指定要鏈接的庫(kù)及表,庫(kù)名可以使用一個(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 多出來(lái)的一個(gè)的一個(gè)虛擬列
a. 它表示原始數(shù)據(jù)的來(lái)源表,它不會(huì)出現(xiàn)在
show table的結(jié)果當(dāng)中
b.select *不會(huì)包含它
?
Distributed
與 Merge 類似, Distributed 也是通過(guò)一個(gè)邏輯表,去訪問(wèn)各個(gè)物理表,設(shè)置引擎時(shí)的樣子是:
Distributed(remote_group, database, table [, sharding_key])
其中:
remote_group/etc/clickhouse-server/config.xml中remote_servers參數(shù)database是各服務(wù)器中的庫(kù)名table是表名sharding_key是一個(gè)尋址表達(dá)式,可以是一個(gè)列名,也可以是像 rand() 之類的函數(shù)調(diào)用,它與 remote_servers 中的 weight 共同作用,決定在 寫(xiě) 時(shí)往哪個(gè) shard 寫(xiě)
配置文件中的 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)重,前面說(shuō)的 sharding_key 與這個(gè)有關(guān)。
簡(jiǎn)單來(lái)說(shuō),上面的配置,理論上來(lái)看:
第一個(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í),一定是寫(xiě)入到第一個(gè) shard 中,如果把 sharding_key 設(shè)置成 rand() ,那系統(tǒng)會(huì)對(duì)應(yīng)地自己作一般化轉(zhuǎn)換吧,這種時(shí)候就是一種概率場(chǎng)景了。internal_replication是定義針對(duì)多個(gè) replica 時(shí)的寫(xiě)入行為的。
如果為 false ,則會(huì)往所有的 replica 中寫(xiě)入數(shù)據(jù),但是并不保證數(shù)據(jù)寫(xiě)入的一致性,所以這種情況時(shí)間一長(zhǎng),各 replica 的數(shù)據(jù)很可能出現(xiàn)差異。如果為 true ,則只會(huì)往第一個(gè)可寫(xiě)的 replica 中寫(xiě)入數(shù)據(jù)(剩下的事“物理表”自己處理)。replica就是定義各個(gè)冗余副本的,選項(xiàng)有 host , port , user , password 等
看一個(gè)實(shí)際的例子,我們先在兩臺(tái)機(jī)器上創(chuàng)建好物理表并插入一些測(cè)試數(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');
在其中一臺(tái)創(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;
注:邏輯表中的寫(xiě)入操作是異步的,會(huì)先緩存在本機(jī)的文件系統(tǒng)上,并且,對(duì)于物理表的不可訪問(wèn)狀態(tài),并沒(méi)有嚴(yán)格控制,所以寫(xiě)入失敗丟數(shù)據(jù)的情況是可能發(fā)生的
?
Null
空引擎,寫(xiě)入的任何數(shù)據(jù)都會(huì)被忽略,讀取的結(jié)果一定是空。
但是注意,雖然數(shù)據(jù)本身不會(huì)被存儲(chǔ),但是結(jié)構(gòu)上的和數(shù)據(jù)格式上的約束還是跟普通表一樣是存在的,同時(shí),你也可以在這個(gè)引擎上創(chuàng)建視圖
?
Buffer
1.Buffer 引擎,像是Memory 存儲(chǔ)的一個(gè)上層應(yīng)用似的(磁盤(pán)上也是沒(méi)有相應(yīng)目錄的)2.它的行為是一個(gè)緩沖區(qū),寫(xiě)入的數(shù)據(jù)先被放在緩沖區(qū),達(dá)到一個(gè)閾值后,這些數(shù)據(jù)會(huì)自動(dòng)被寫(xiě)到指定的另一個(gè)表中3.和Memory 一樣,有很多的限制,比如沒(méi)有索引4.Buffer 是接在其它表前面的一層,對(duì)它的讀操作,也會(huì)自動(dòng)應(yīng)用到后面表,但是因?yàn)榍懊嬲f(shuō)到的限制的原因,一般我們讀數(shù)據(jù),就直接從源表讀就好了,緩沖區(qū)的這點(diǎn)數(shù)據(jù)延遲,只要配置得當(dāng),影響不大的5.Buffer 后面也可以不接任何表,這樣的話,當(dāng)數(shù)據(jù)達(dá)到閾值,就會(huì)被丟棄掉
一些特點(diǎn):
- 如果一次寫(xiě)入的數(shù)據(jù)太大或太多,超過(guò)了 max 條件,則會(huì)直接寫(xiě)入源表。
- 刪源表或改源表的時(shí)候,建議 Buffer 表刪了重建。
- “友好重啟”時(shí), Buffer 數(shù)據(jù)會(huì)先落到源表,“暴力重啟”, Buffer 表中的數(shù)據(jù)會(huì)丟失。
- 即使使用了 Buffer ,多次的小數(shù)據(jù)寫(xiě)入,對(duì)比一次大數(shù)據(jù)寫(xiě)入,也 慢得多 (幾千行與百萬(wàn)行的差距)
-- 創(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ù)庫(kù)table 源表,這里除了字符串常量,也可以使用變量的。num_layers 是類似“分區(qū)”的概念,每個(gè)分區(qū)的后面的 min / max 是獨(dú)立計(jì)算的,官方推薦的值是 16 。min / max 這組配置薦,就是設(shè)置閾值的,分別是 時(shí)間(秒),行數(shù),空間(字節(jié))。
閾值的規(guī)則: 是“所有的 min 條件都滿足, 或 至少一個(gè) max 條件滿足”。
如果按上面我們的建表來(lái)說(shuō),所有的 min 條件就是:過(guò)了 3秒,2條數(shù)據(jù),1 Byte。一個(gè) max 條件是:20秒,或 10 條數(shù)據(jù),或有 10K
?
Set
Set 這個(gè)引擎有點(diǎn)特殊,因?yàn)樗挥迷?IN 操作符右側(cè),你不能對(duì)它 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 需要手動(dòng)進(jìn)行類型轉(zhuǎn)換
select 1 where (toUInt16(1), 'hello') in test.set;
注:Set 引擎表,是全內(nèi)存運(yùn)行的,但是相關(guān)數(shù)據(jù)會(huì)落到磁盤(pán)上保存,啟動(dòng)時(shí)會(huì)加載到內(nèi)存中。所以,意外中斷或暴力重啟,是可能產(chǎn)生數(shù)據(jù)丟失問(wèn)題的
?
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ù)場(chǎng)景2.相比 MergeTree ,ReplacingMergeTree 在最后加一個(gè)"版本列",它跟時(shí)間列配合一起,用以區(qū)分哪條數(shù)據(jù)是"新的",并把舊的丟掉(這個(gè)過(guò)程是在 merge 時(shí)處理,不是數(shù)據(jù)寫(xiě)入時(shí)就處理了的,平時(shí)重復(fù)的數(shù)據(jù)還是保存著的,并且查也是跟平常一樣會(huì)查出來(lái)的)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,可以手動(dòng)觸發(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求和的列可以指定,不可加的未指定列,會(huì)取一個(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);-- 手動(dòng)觸發(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 ,則這行會(huì)被刪除
?
AggregatingMergeTree
AggregatingMergeTree 是在 MergeTree 基礎(chǔ)之上,針對(duì)聚合函數(shù)結(jié)果,作增量計(jì)算優(yōu)化的一個(gè)設(shè)計(jì),它會(huì)在 merge 時(shí),針對(duì)主鍵預(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 |
通過(guò)減少一個(gè)維度的方式,來(lái)以 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ì)算
對(duì)于 AggregatingMergeTree 引擎的表,不能使用普通的 INSERT 去添加數(shù)據(jù),可以用:a. INSERT SELECT 來(lái)插入數(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;-- 手動(dòng)觸發(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
是專門(mén)為 OLAP 場(chǎng)景下,一種“變通”存數(shù)做法而設(shè)計(jì)的,在數(shù)據(jù)是不能改,更不能刪的前提下,通過(guò)“運(yùn)算”的方式,去抹掉舊數(shù)據(jù)的影響,把舊數(shù)據(jù)“減”去即可,從而解決"最終狀態(tài)"類的問(wèn)題,比如 當(dāng)前有多少人在線?
“以加代刪”的增量存儲(chǔ)方式,帶來(lái)了聚合計(jì)算方便的好處,代價(jià)卻是存儲(chǔ)空間的翻倍,并且,對(duì)于只關(guān)心最新?tīng)顟B(tài)的場(chǎng)景,中間數(shù)據(jù)都是無(wú)用的
CollapsingMergeTree 在創(chuàng)建時(shí)與 MergeTree 基本一樣,除了最后多了一個(gè)參數(shù),需要指定 Sign 位(必須是 Int8 類型)
create table test.collapsingmergetree(sign Int8, sdt Date, name String, cnt UInt16) ENG
?
總結(jié)
以上是生活随笔為你收集整理的clickhouse 基础知识的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2022-2028年中国新能源环卫车行业
- 下一篇: Linux 忘记root登录密码解决方法