表引擎(即表的類型)決定了:
1)數據的存儲方式和位置,寫到哪里以及從哪里讀取數據
2)支持哪些查詢以及如何支持。
3)并發數據訪問。
4)索引的使用(如果存在)。
5)是否可以執行多線程請求。
6)數據復制參數。
ClickHouse的表引擎有很多,下面介紹其中幾種,對其他引擎有興趣的可以去查閱官方文檔:https://clickhouse.yandex/docs/zh/operations/table_engines/
4.1 TinyLog
最簡單的表引擎,用于將數據存儲在磁盤上。每列都存儲在單獨的壓縮文件中,寫入時,數據將附加到文件末尾。
該引擎沒有并發控制
- 如果同時從表中讀取和寫入數據,則讀取操作將拋出異常;
- 如果同時寫入多個查詢中的表,則數據將被破壞。
這種表引擎的典型用法是 write-once:首先只寫入一次數據,然后根據需要多次讀取。此引擎適用于相對較小的表(建議最多1,000,000行)。如果有許多小表,則使用此表引擎是適合的,因為它比需要打開的文件更少。當擁有大量小表時,可能會導致性能低下。 不支持索引。
案例:創建一個TinyLog引擎的表并插入一條數據
:)create table t (a UInt16, b String) ENGINE=TinyLog;
:)insert into t (a, b) values (1, 'abc');
此時我們到保存數據的目錄/var/lib/clickhouse/data/default/t中可以看到如下目錄結構:
[root@hadoop102 t]# ls
a.bin b.bin sizes.json
a.bin 和 b.bin 是壓縮過的對應的列的數據, sizes.json 中記錄了每個 *.bin 文件的大?。?/p>
[root@hadoop102 t]# cat sizes.json
{"yandex":{"a%2Ebin":{"size":"28"},"b%2Ebin":{"size":"30"}}}
4.2 Memory
內存引擎,數據以未壓縮的原始形式直接保存在內存當中,服務器重啟數據就會消失。讀寫操作不會相互阻塞,不支持索引。簡單查詢下有非常非常高的性能表現(超過10G/s)。
一般用到它的地方不多,除了用來測試,就是在需要非常高的性能,同時數據量又不太大(上限大概 1 億行)的場景。
4.3 Merge
Merge 引擎 (不要跟 MergeTree 引擎混淆) 本身不存儲數據,但可用于同時從任意多個其他的表中讀取數據。 讀是自動并行的,不支持寫入。讀取時,那些被真正讀取到數據的表的索引(如果有的話)會被使用。
Merge 引擎的參數:一個數據庫名和一個用于匹配表名的正則表達式。
案例:先建t1,t2,t3三個表,然后用 Merge 引擎的 t 表再把它們鏈接起來。
:)create table t1 (id UInt16, name String) ENGINE=TinyLog;
:)create table t2 (id UInt16, name String) ENGINE=TinyLog;
:)create table t3 (id UInt16, name String) ENGINE=TinyLog;
:)insert into t1(id, name) values (1, 'first');
:)insert into t2(id, name) values (2, 'second');
:)insert into t3(id, name) values (3, 'i am in t3');
:)create table t (id UInt16, name String)
ENGINE=Merge(currentDatabase(), '^t');
:) select * from t;
┌─id─┬─name─┐
│ 2 │ second │
└────┴──────┘
┌─id─┬─name──┐
│ 1 │ first │
└────┴───────┘
┌─id─┬─name───────┐
│ 3 │ i am in t3 │
└────┴────────────┘
4.4 MergeTree
Clickhouse 中最強大的表引擎當屬 MergeTree (合并樹)引擎及該系列(*MergeTree)中的其他引擎。
MergeTree 引擎系列的基本理念如下。當你有巨量數據要插入到表中,你要高效地一批批寫入數據片段,并希望這些數據片段在后臺按照一定規則合并。相比在插入時不斷修改(重寫)數據進存儲,這種策略會高效很多。
格式:
ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity)
參數解讀:
date-column — 類型為 Date 的列名。ClickHouse 會自動依據這個列按月創建分區。分區名格式為 "YYYYMM" 。
sampling_expression — 采樣表達式。
(primary, key) — 主鍵。類型為Tuple()
index_granularity — 索引粒度。即索引中相鄰”標記”間的數據行數。設為 8192 可以適用大部分場景。
案例:
create table mt_table (date Date, id UInt8, name String) ENGINE=MergeTree()
partition by date
order by (id, name)
settings index_granularity=8192;
insert into mt_table values ('2019-05-01', 1, 'zhangsan');
insert into mt_table values ('2019-06-01', 2, 'lisi');
insert into mt_table values ('2019-05-03', 3, 'wangwu');
在/var/lib/clickhouse/data/default/mt_tree下可以看到:
[root@hadoop102 mt_table]# ls
20190501_20190501_2_2_0
20190503_20190503_6_6_0
20190601_20190601_4_4_0
detached
隨便進入一個目錄:
[root@hadoop102 20190601_20190601_4_4_0]# ls
checksums.txt columns.txt date.bin date.mrk id.bin id.mrk name.bin name.mrk primary.idx
- *.bin是按列保存數據的文件
- *.mrk保存塊偏移量
- primary.idx保存主鍵索引
4.5 ReplacingMergeTree
這個引擎是在 MergeTree 的基礎上,添加了“處理重復數據”的功能,該引擎和MergeTree的不同之處在于它會刪除具有相同主鍵的重復項。數據的去重只會在合并的過程中出現。合并會在未知的時間在后臺進行,所以你無法預先作出計劃。有一些數據可能仍未被處理。因此,ReplacingMergeTree 適用于在后臺清除重復的數據以節省空間,但是它不保證沒有重復的數據出現。
格式:
ENGINE [=] ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver])
可以看出他比MergeTree只多了一個ver,這個ver指代版本列。
案例:
create table rmt_table (date Date, id UInt8, name String,point UInt8) ENGINE= ReplacingMergeTree(date, (id, name), 8192,point);
插入一些數據:
insert into rmt_table values ('2019-07-10', 1, 'a', 20);
insert into rmt_table values ('2019-07-10', 1, 'a', 30);
insert into rmt_table values ('2019-07-11', 1, 'a', 20);
insert into rmt_table values ('2019-07-11', 1, 'a', 30);
insert into rmt_table values ('2019-07-11', 1, 'a', 10);
等待一段時間或optimize table rmt_table手動觸發merge,后查詢
:) select * from rmt_table;
┌───────date─┬─id─┬─name─┬─point─┐
│ 2019-07-11 │ 1 │ a │ 30 │
└────────────┴────┴──────┴───────┘
4.6 SummingMergeTree
該引擎繼承自 MergeTree。區別在于,當合并 SummingMergeTree 表的數據片段時,ClickHouse 會把所有具有相同主鍵的行合并為一行,該行包含了被合并的行中具有數值數據類型的列的匯總值。如果主鍵的組合方式使得單個鍵值對應于大量的行,則可以顯著的減少存儲空間并加快數據查詢的速度,對于不可加的列,會取一個最先出現的值。
語法:
ENGINE [=] SummingMergeTree(date-column [, sampling_expression],
(primary, key), index_granularity, [columns])
columns — 包含將要被匯總的列的列名的元組
案例:
create table smt_table (date Date, name String, a UInt16, b UInt16) ENGINE=SummingMergeTree(date, (date, name), 8192, (a))
插入數據:
insert into smt_table (date, name, a, b) values ('2019-07-10', 'a', 1, 2);
insert into smt_table (date, name, a, b) values ('2019-07-10', 'b', 2, 1);
insert into smt_table (date, name, a, b) values ('2019-07-11', 'b', 3, 8);
insert into smt_table (date, name, a, b) values ('2019-07-11', 'b', 3, 8);
insert into smt_table (date, name, a, b) values ('2019-07-11', 'a', 3, 1);
insert into smt_table (date, name, a, b) values ('2019-07-12', 'c', 1, 3);
等待一段時間或optimize table smt_table手動觸發merge,后查詢
:) select * from smt_table
┌───────date─┬─name─┬─a─┬─b─┐
│ 2019-07-10 │ a │ 1 │ 2 │
│ 2019-07-10 │ b │ 2 │ 1 │
│ 2019-07-11 │ a │ 3 │ 1 │
│ 2019-07-11 │ b │ 6 │ 8 │
│ 2019-07-12 │ c │ 1 │ 3 │
└────────────┴──────┴───┴───┘
發現2019-07-11,b的a列合并相加了,b列取了8(因為b列為8的數據最先插入)。
4.7 Distributed
分布式引擎,本身不存儲數據, 但可以在多個服務器上進行分布式查詢。 讀是自動并行的。讀取時,遠程服務器表的索引(如果有的話)會被使用。
Distributed(cluster_name, database, table [, sharding_key])
參數解析:
cluster_name - 服務器配置文件中的集群名,在/etc/metrika.xml中配置的
database – 數據庫名
table – 表名
sharding_key – 數據分片鍵
案例演示:
1)在hadoop102,hadoop103,hadoop104上分別創建一個表t
:) create table t(id UInt16, name String) ENGINE=TinyLog;
2)在三臺機器的t表中插入一些數據
insert into t(id, name) values (1, 'zhangsan');
insert into t(id, name) values (2, 'lisi');
3)在hadoop102上創建分布式表
:) create table dis_table(id UInt16, name String) ENGINE=Distributed(clickhouse_cluser, default, t, id);
clickhouse_cluser
4)往dis_table中插入數據
:) insert into dis_table select * from t
5)查看數據量
:) select count() from dis_table
FROM dis_table
┌─count()─┐
│ 8 │
└─────────┘
:) select count() from t
SELECT count()
FROM t
┌─count()─┐
│ 3 │
└─────────┘
可以看到每個節點大約有1/3的數據