1.介绍
Clickhouse 中最强大的表引擎当属 MergeTree (合并树)引擎及该系列(MergeTree)中的其他引擎。MergeTree 允许依据主键和日期创建索引,并进行实时的数据更新操作。MergeTree 是 ClickHouse 里最为先进的表引擎。
请注意不要将 MergeTree 跟 Merge引擎混淆!!!
MergeTree 引擎系列的基本理念如下。当你有巨量数据要插入到表中,你要高效地一批批写入数据片段,并希望这些数据片段在后台按照一定规则合并。相比在插入时不断修改(重写)数据进存储,这种策略会高效很多。
主要特点:
存储的数据按主键排序。
这让你可以创建一个用于快速检索数据的小稀疏索引。
允许使用分区,如果指定了分区键的话。
在相同数据集和相同结果集的情况下 ClickHouse 中某些带分区的操作会比普通操作更快。查询中指定了分区键时 ClickHouse 会自动截取分区数据。这也有效增加了查询性能。
支持数据副本。
ReplicatedMergeTree 系列的表便是用于此。更多信息,请参阅官方文档。
支持数据采样。
需要的话,你可以给表设置一个采样方法。
不使用采样表达式的例子:
MergeTree(EventDate, (CounterID, EventDate), 8192)*
使用采样表达式的例子:
MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID)), 8192)*
2. 建表
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
MergeTree 引擎在创建时接收以下4个参数,
日期字段的名称 (索引字段)
采样表达式 (可选的)
含有主键相关字段的元组
稀疏索引的粒度(见下文)。
以 MergeTree 作为引擎的数据表必须含有一个独立的 Date 字段。比如说, EventDate 字段。这个日期字段必须是 Date 类型的(非 DateTime 类型)。
主键可以是任意表达式构成的元组(通常是列名称的元组),或者是单独一个字段。
抽样表达式(可选的)可以是任意表达式。这个表达式必须在主键中。上面的例子使用了 CounterID 的哈希 intHash32 作为采样表达式,旨在近乎随机地在 CounterID 和 EventDate 内打乱数据条目。换而言之,当我们在查询中使用 SAMPLE 子句时,我们就可以得到一个近乎随机分布的用户列表。
数据表将数据分割为小的索引块作为单位进行处理。 每个索引块之间依照主键排序。每个索引块记录了指定的开始日期和结束日期。在您插入数据时,MergeTree 就会对数据进行排序处理,以保证存储在索引块内的数据有序。 索引块之间的合并过程会在系统后台定期自动执行。MergeTree 引擎会选择几个相邻的索引块进行合并(通常是较小的索引块), 然后对二者合并、排序。
具体而言, 向 MergeTree 表中插入数据时,引擎会首先对新数据执行递增排序而保存索引块;其后,数据索引块之间又会进一步合并,以减少总体索引块数量。 因此,合并过程本身并无过多排序工作。
3.主键和索引在查询中的表现
我们以 (CounterID, Date) 以主键。排序好的索引的图示会是下面这样:
全部数据 : [-------------------------------------------------------------------------]
CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date: [1111111222222233331233211111222222333211111112122222223111112223311122333]
标记: | | | | | | | | | | |
a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3
标记号: 0 1 2 3 4 5 6 7 8 9 10
如果指定查询如下:
CounterID in (‘a’,’h’),服务器会读取标记号在 [0, 3) 和 [6, 8) 区间中的数据。
CounterID IN (‘a’,’h’) AND Date = 3,服务器会读取标记号在 [1, 3) 和 [7, 8) 区间中的数据。
Date = 3,服务器会读取标记号在 [1, 10] 区间中的数据。
上面例子可以看出使用索引通常会比全表描述要高效。
稀疏索引会引起额外的数据读取。当读取主键单个区间范围的数据时,每个数据块中最多会多读 index_granularity * 2 行额外的数据。大部分情况下,当 index_granularity = 8192 时,ClickHouse的性能并不会降级。
稀疏索引让你能操作有巨量行的表。因为这些索引是常驻内存(RAM)的。
ClickHouse 不要求主键惟一。所以,你可以插入多条具有相同主键的行。