表引擎(即表的類型)決定了:
1)數據的存儲方式和位置,寫到哪里以及從哪里讀取數據
2)支持哪些查詢以及如何支持。
3)並發數據訪問。
4)索引的使用(如果存在)。
5)是否可以執行多線程請求。
6)數據復制參數。
ClickHouse的表引擎有很多,下面介紹其中幾種,對其他引擎有興趣的可以去查閱官方文檔:https://clickhouse.yandex/docs/zh/operations/table_engines/
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 文件的大小:
[root@hadoop102 t]# cat sizes.json
{"yandex":{"a%2Ebin":{"size":"28"},"b%2Ebin":{"size":"30"}}}
2 、Memory
內存引擎,數據以未壓縮的原始形式直接保存在內存當中,服務器重啟數據就會消失。讀寫操作不會相互阻塞,不支持索引。簡單查詢下有非常非常高的性能表現(超過10G/s)。
一般用到它的地方不多,除了用來測試,就是在需要非常高的性能,同時數據量又不太大(上限大概 1 億行)的場景。
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、MergeTree
Clickhouse 中最強大的表引擎當屬 MergeTree (合並樹)引擎及該系列(*MergeTree)中的其他引擎。
MergeTree 引擎系列的基本理念如下。當你有巨量數據要插入到表中,你要高效地一批批寫入數據片段,並希望這些數據片段在后台按照一定規則合並。相比在插入時不斷修改(重寫)數據進存儲,這種策略會高效很多。
主要特點:
1.存儲的數據按主鍵排序。
2.這使得你能夠創建一個小型的稀疏索引來加快數據檢索。
3.支持數據分區,如果指定了 分區鍵 的話。
在相同數據集和相同結果集的情況下 ClickHouse 中某些帶分區的操作會比普通操作更快。查詢中指定了分區鍵時 ClickHouse 會自動截取分區數據。這也有效增加了查詢性能。
支持數據副本。
4.ReplicatedMergeTree 系列的表提供了數據副本功能
5.支持數據采樣。需要的話,你可以給表設置一個采樣方法。
注意:合並 引擎並不屬於 *MergeTree 系列
語法:
ENGINE MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity=8192
參數解讀:
ENGINE - 引擎名和參數。 ENGINE = MergeTree(). MergeTree 引擎沒有參數。
ORDER BY — 排序鍵。
可以是一組列的元組或任意的表達式。 例如: ORDER BY (CounterID, EventDate) 。
如果沒有使用 PRIMARY KEY 顯式的指定主鍵,ClickHouse 會使用排序鍵作為主鍵。
如果不需要排序,可以使用 ORDER BY tuple(). 參考 選擇主鍵
PARTITION BY — 分區鍵 。
要按月分區,可以使用表達式 toYYYYMM(date_column) ,這里的 date_column 是一個 Date 類型的列。分區名的格式會是 "YYYYMM" 。
PRIMARY KEY - 主鍵,如果要 選擇與排序鍵不同的主鍵,可選。
默認情況下主鍵跟排序鍵(由 ORDER BY 子句指定)相同。
因此,大部分情況下不需要再專門指定一個 PRIMARY KEY 子句。
SAMPLE BY — 用於抽樣的表達式。
如果要用抽樣表達式,主鍵中必須包含這個表達式。例如:
SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID)) 。
TTL 指定行存儲的持續時間並定義數據片段在硬盤和卷上的移動邏輯的規則列表,可選。
表達式中必須存在至少一個 Date 或 DateTime 類型的列,比如:
TTL date + INTERVAl 1 DAY
規則的類型 DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'指定了當滿足條件(到達指定時間)時所要執行的動作:移除過期的行,還是將數據片段(如果數據片段中的所有行都滿足表達式的話)移動到指定的磁盤(TO DISK 'xxx') 或 卷(TO VOLUME 'xxx')。默認的規則是移除(DELETE)。可以在列表中指定多個規則,但最多只能有一個DELETE的規則。
更多細節,請查看 表和列的 TTL
SETTINGS — 控制 MergeTree 行為的額外參數:
index_granularity — 索引粒度。索引中相鄰的『標記』間的數據行數。默認值,8192 。參考數據存儲。
index_granularity_bytes — 索引粒度,以字節為單位,默認值: 10Mb。如果想要僅按數據行數限制索引粒度, 請設置為0(不建議)。
enable_mixed_granularity_parts — 是否啟用通過 index_granularity_bytes 控制索引粒度的大小。在19.11版本之前, 只有 index_granularity 配置能夠用於限制索引粒度的大小。當從具有很大的行(幾十上百兆字節)的表中查詢數據時候,index_granularity_bytes 配置能夠提升ClickHouse的性能。如果你的表里有很大的行,可以開啟這項配置來提升SELECT 查詢的性能。
use_minimalistic_part_header_in_zookeeper — 是否在 ZooKeeper 中啟用最小的數據片段頭 。如果設置了 use_minimalistic_part_header_in_zookeeper=1 ,ZooKeeper 會存儲更少的數據。更多信息參考『服務配置參數』這章中的 設置描述 。
min_merge_bytes_to_use_direct_io — 使用直接 I/O 來操作磁盤的合並操作時要求的最小數據量。合並數據片段時,ClickHouse 會計算要被合並的所有數據的總存儲空間。如果大小超過了 min_merge_bytes_to_use_direct_io 設置的字節數,則 ClickHouse 將使用直接 I/O 接口(O_DIRECT 選項)對磁盤讀寫。如果設置 min_merge_bytes_to_use_direct_io = 0 ,則會禁用直接 I/O。默認值:10 * 1024 * 1024 * 1024 字節。
merge_with_ttl_timeout — TTL合並頻率的最小間隔時間,單位:秒。默認值: 86400 (1 天)。
write_final_mark — 是否啟用在數據片段尾部寫入最終索引標記。默認值: 1(不建議更改)。
merge_max_block_size — 在塊中進行合並操作時的最大行數限制。默認值:8192
storage_policy — 存儲策略。 參見 使用具有多個塊的設備進行數據存儲.
min_bytes_for_wide_part,min_rows_for_wide_part 在數據片段中可以使用Wide格式進行存儲的最小字節數/行數。你可以不設置、只設置一個,或全都設置。參考:數據存儲
案例:
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保存主鍵索引
5、 ReplacingMergeTree
這個引擎是在 MergeTree 的基礎上,添加了“處理重復數據”的功能,該引擎和MergeTree的不同之處在於它會刪除具有相同主鍵的重復項。數據的去重只會在合並的過程中出現。合並會在未知的時間在后台進行,所以你無法預先作出計划。有一些數據可能仍未被處理。因此,ReplacingMergeTree 適用於在后台清除重復的數據以節省空間,但是它不保證沒有重復的數據出現。
語法:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = ReplacingMergeTree([ver])
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
參數
ver — 版本列。類型為 UInt*, Date 或 DateTime。可選參數。
合並的時候,`ReplacingMergeTree` 從所有具有相同主鍵的行中選擇一行留下:
- 如果 `ver` 列未指定,選擇最后一條。
- 如果 `ver` 列已指定,選擇 `ver` 值最大的版本。
案例:
create table rmt_table (date Date, id UInt8, name String,point UInt8)
ENGINE= ReplacingMergeTree(point)
partition by date
order by (id,name)
SETTINGS index_granularity=8192;
插入一些數據:
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 │
└────────────┴────┴──────┴───────┘
6、 SummingMergeTree
該引擎繼承自 MergeTree。區別在於,當合並 SummingMergeTree 表的數據片段時,ClickHouse 會把所有具有相同主鍵的行合並為一行,該行包含了被合並的行中具有數值數據類型的列的匯總值。如果主鍵的組合方式使得單個鍵值對應於大量的行,則可以顯著的減少存儲空間並加快數據查詢的速度,對於不可加的列,會取一個最先出現的值。
語法:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = SummingMergeTree([columns])
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
SummingMergeTree 的參數:
columns - 包含了將要被匯總的列的列名的元組。可選參數。
所選的列必須是數值類型,並且不可位於主鍵中。
如果沒有指定 `columns`,ClickHouse 會把所有不在主鍵中的數值類型的列都進行匯總。
案例:
create table smt_table (date Date, name String, sum UInt16, not_sum UInt16)
ENGINE= ReplacingMergeTree(sum)
partition by date
order by (date,name)
SETTINGS index_granularity=8192;
插入數據:
insert into smt_table (date, name, sum, not_sum ) values ('2019-07-10', 'a', 1, 2);
insert into smt_table (date, name, sum, not_sum ) values ('2019-07-10', 'b', 2, 1);
insert into smt_table (date, name, sum, not_sum ) values ('2019-07-11', 'b', 3, 8);
insert into smt_table (date, name, sum, not_sum ) values ('2019-07-11', 'b', 3, 8);
insert into smt_table (date, name, sum, not_sum ) values ('2019-07-11', 'a', 3, 1);
insert into smt_table (date, name, sum, not_sum ) 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的數據最先插入)。
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(perftest_3shards_1replicas, default, t, id);
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的數據
