0
点赞
收藏
分享

微信扫一扫

ClickHouse最强表引擎MergeTree

1. 概述

Clickhouse 中最强大的表引擎当属 MergeTree 引擎及该系列 (*MergeTree) 中的其他引擎。只有 MergeTree 系列的表引擎才支持主键索引,数据分区,数据副本,数据采样这些特性,只有此系列的表引擎才支持ALTER操作。

MergeTree 系列引擎用于插入极大量的数据到一张表当中。数据以片段的形式快速写入,然后定期在后台按照规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。

2. 创建表

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2,
...
PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
[DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name=value, ...]
  • ENGINE: 指定引擎名称和参数,MergeTree 引擎没有参数。
  • ORDER BY: 排序键,可以是多个列的组合。如果没有通过PRIMARY KEY指定主键,则以此参数作为主键。如果无需排序,则使用ORDER BY tuple()。
  • PARTITION BY: (可选) 分区键。分区不会加速查询,这一点与ORDER BY相反。但是合理使用分区,可以减少查询数据文件的扫描范围。例如以月来分区,可以写为toYYYYMM(date_column),其中date_column是Date类型的列,分区名称的格式为YYYYMM。
  • PRIMARY KEY: (可选) 主键。声明后会按照主键字段生成一级索引,用于加速表查询。默认情况下主键与排序键相同,所以通常直接使用ORDER BY指定主键,无须刻意通过PRIMARY KEY声明。MergeTree 主键允许存在重复 (ReplacingMergeTree 可以去重)。
  • SAMPLE BY: (可选) 抽样表达式。用于声明数据以何种标准进行采样。若使用了此配置,则在主键的配置中也需要声明同样的表达式,例如SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))。抽样表达式需要配合SAMPLE BY子查询使用,这项功能对于选取抽样数据十分有用。
  • TTL: (可选) 指定表或列级别的数据存活策略,可以删除数据或将数据移动到 DISK 或 VOLUME。表达式中必须包含Date或DateTime类型的列,例如TTL time_column + INTERVAL 1 DAY表示数据的存活时间为time_column到期的1天之后。
  • SETTINGS: (可选) 其他设置。
    • index_granularity: 索引的颗粒度,默认值 8192,表示每隔 8192 行数据才生成一条索引。通常不需要修改此参数。
    • index_granularity_bytes: 索引的最大颗粒度。默认值 10Mb,设置为 0 表示仅根据行数限制颗粒度 (不推荐)。
    • storage_policy: 存储策略,详见 Using Multiple Block Devices for Data Storage

3. 数据存储方式

表由按主键排序的数据片段 (data parts) 组成,每个数据片段被逻辑分割成颗粒 (granules),这是 ClickHouse 查询时的最小不可分割数据集。 ClickHouse 不会对行或值进行拆分,所以每个颗粒总是包含整数个行。每个颗粒的第一行通过主键进行标记, ClickHouse 会为每个数据片段创建一个索引文件来存储这些标记。对于每列,无论它是否包含在主键当中,ClickHouse 都会存储类似标记。这些标记使 ClickHouse 可以在列文件中直接找到数据。

颗粒的大小由表引擎参数 index_granularityindex_granularity_bytes 控制。颗粒的行数的在 [1, index_granularity] 范围中,这取决于行的大小。如果单行的大小超过了 index_granularity_bytes 设置的值,那么一个颗粒的大小会超过 index_granularity_bytes。此时,颗粒的大小等于该行的大小。

数据片段可以以 WideCompact 格式存储。在 Wide 格式下,每一列都会在文件系统中存储为单独的文件,在 Compact 格式下所有列都存储在一个文件中。Compact 格式可以提高插入量少插入频率频繁时的性能。

4. 主键与索引

4.1 主键的选择

主键中列的数量并没有明确的限制,长主键会对插入性能和内存消耗有负面影响,但主键中额外的列并不影响 SELECT 查询的性能。

使用 ORDER BY tuple() 可以创建没有主键的表,这时 ClickHouse 根据数据插入的顺序存储。如果在使用 INSERT ... SELECT 时希望保持数据的排序,可设置 max_insert_threads = 1

4.2 选择与排序键不同的主键

当主键与排序键不同时,排序键用于在数据片段 (data parts) 中进行排序,主键用于在索引文件中进行标记的写入。这种情况下,主键表达式元组必须是排序键表达式元组的前缀 (即主键为 (a,b),排序列须为 (a,b,*))。

当使用 SummingMergeTreeAggregatingMergeTree 引擎时,这个特性非常有用。通常在使用这类引擎时,表里的列分两种:维度 (dimension) 和 度量 (measure) ,典型的查询会通过任意的 GROUP BYmeasure 列进行聚合并通过 dimension 列进行过滤。 SummingMergeTreeAggregatingMergeTree 会对排序键相同的行进行聚合,因此把所有的 dimension 放进排序键是很自然的做法,但这将导致排序键中包含大量的列,并且排序键会随着新添加的 measure 不断更新。

在这种情况下合理的做法是,只保留少量的列在主键当中用于提升扫描效率,将 dimension 列添加到排序键中。

对排序键进行 ALTER 是轻量级的操作,因为当一个新列同时被加入到表里和排序键里时,已存在的数据片段并不需要修改。由于旧的排序键是新排序键的前缀,并且新添加的列中没有数据,因此在表修改时的数据对于新旧的排序键来说都是有序的。

4.3 索引和分区在查询中的应用

SELECT 查询中,如果 WHERE/PREWHERE 子句具有下面这些表达式,则可以使用索引:

进行相等/不相等的比较;

  • 对主键列或分区列 (partitioning key) 进行 IN 运算;
  • 有固定前缀的 LIKE 运算(如 name like 'test%');
  • 函数运算 (部分函数适用);
  • 对上述表达式进行逻辑运算。

当引擎配置如下时:

ENGINE MergeTree() 
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate)
SETTINGS index_granularity=8192

以下查询会依据主键索引剪掉不符合的数据,依据按月分区的分区键剪掉那些不包含符合数据的分区

SELECT count() FROM table WHERE EventDate = toDate(now()) AND CounterID = 34
SELECT count() FROM table WHERE EventDate = toDate(now()) AND (CounterID = 34 OR CounterID = 42)
SELECT count() FROM table WHERE ((EventDate >= toDate('2014-01-01') AND EventDate <= toDate('2014-01-31')) OR EventDate = toDate('2014-05-01')) AND CounterID IN (101500, 731962, 160656) AND (CounterID = 101500 OR EventDate != toDate('2014-05-01'))

4.4 跳数索引 (Data Skipping Index)

语法:

INDEX index_name expr TYPE type(...) GRANULARITY granularity_value

*MergeTree 系列的表可以指定跳数索引。跳数索引是指数据片段按照粒度 (建表时指定的 index_granularity) 分割成小块后,将上述 SQL 语法中的 granularity_value 数量的小块组合成一个大块,对这些大块写入索引信息,这样有助于使用 where 筛选时跳过大量不必要的数据,减少 SELECT 需要读取的数据量。

CREATE TABLE table_name
(
u64 UInt64,
i32 Int32,
s String,
...
INDEX a (u64 * i32, s) TYPE minmax GRANULARITY 3,
INDEX b (u64 * length(s)) TYPE set(1000) GRANULARITY 4
) ENGINE = MergeTree()

在执行以下查询时能减少读取数据量:

SELECT count() FROM table WHERE s < 'z'
SELECT count() FROM table WHERE u64 * i32 == 10 AND u64 * length(s) >= 1234

索引类型:

  • minmax: 存储指定表达式的极值 (如果表达式是 tuple,则存储 tuple 中每个元素的极值),用于跳过数据块,类似主键。
  • set(max_rows): 存储指定表达式的不重复值 (不超过 max_rows 个,max_rows=0 表示无限制),用于检查数据块是否满足 WHERE 条件。
  • ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 存储一个包含数据块中所有 n元短语 (ngram) 的布隆过滤器,只可用在字符串上。可用于优化 equals,like 和 in 表达式的性能。 n: 短语长度
    • size_of_bloom_filter_in_bytes: 布隆过滤器大小,单位为字节。(因为压缩得好,可以指定较大的值,如 256 或 512)
    • number_of_hash_functions: 布隆过滤器中使用的哈希函数的个数
    • random_seed: 哈希函数的随机种子
  • tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 跟 ngrambf_v1 类似,但是存储的是 token 而不是 ngrams。Token是由非字母数字的符号分割的序列。
  • bloom_filter([false_positive]): 为指定的列存储布隆过滤器

WHERE 子句中的条件可以包含对某列数据进行运算的函数表达式,如果列是索引的一部分,ClickHouse会在执行函数时尝试使用索引。不同的函数对索引的支持是不同的。具体查看官网:https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#functions-support

5. 列和表的 TTL

TTL用于设置值的生命周期,可用于列或表的级别。 TTL 表达式的计算结果必须是 Date 或 DateTime 类型的字段。

TTL time_column
TTL time_column + interval

要定义interval, 需要使用 INTERVAL 操作符:

TTL date_time + INTERVAL 1 MONTH
TTL date_time + INTERVAL 15 HOUR

5.1 列 TTL

当列中的值过期时, 会被替换成该列数据类型的默认值。如果数据片段中列的所有值均已过期,则 ClickHouse 会从数据片段中删除此列。 TTL 子句不能被用于主键字段。

-- 建表时指定TTL
CREATE TABLE example_table
(
d DateTime,
a Int TTL d + INTERVAL 1 MONTH,
b Int TTL d + INTERVAL 1 MONTH,
c String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d;

-- 为表中已存在的列添加TTL
ALTER TABLE example_table
MODIFY COLUMN
c String TTL d + INTERVAL 1 DAY;

-- 修改列的TTL
ALTER TABLE example_table
MODIFY COLUMN
c String TTL d + INTERVAL 1 MONTH;

5.2 表 TTL

表可以设置一个用于移除过期行的表达式,以及多个用于在磁盘或卷上自动转移数据片段的表达式。 当表中的行过期时,ClickHouse 会删除所有对应的行。对于数据片段的转移特性,必须所有的行都满足转移条件。

TTL expr
[DELETE|RECOMPRESS codec_name1|TO DISK 'xxx'|TO VOLUME 'xxx'][, DELETE|RECOMPRESS codec_name2|TO DISK 'aaa'|TO VOLUME 'bbb'] ...
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ]
  • DELETE: 删除过期的行 (默认操作);
  • RECOMPRESS codec_name: 以指定的编码重新压缩;
  • TO DISK 'aaa': 将数据片段移动到磁盘 aaa;
  • TO VOLUME 'bbb': 将数据片段移动到卷 bbb;
  • GROUP BY: 聚合过期的行

使用 WHERE 可以指定哪些过期的行会被删除或聚合 (不适用于移动)。GROUP BY 必须是表主键的前缀。如果某列不是 GROUP BY 表达式的一部分,也没有在 SET 从句显示引用,结果行中相应列的值是随机的 (就好像使用了 any 函数)。

-- 创建表时指定TTL
CREATE TABLE example_table
(
d DateTime,
a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH [DELETE],
d + INTERVAL 1 WEEK TO VOLUME 'aaa',
d + INTERVAL 2 WEEK TO DISK 'bbb';

-- 修改表的TTL
ALTER TABLE example_table
MODIFY TTL d + INTERVAL 1 DAY;

5.3 删除过期数据

已过期的数据在数据片段合并时会被删除。如果在两次合并的时间间隔中执行 SELECT 查询, 则可能会得到过期的数据。为了避免这种情况,可以在 SELECT 之前使用 OPTIMIZE

6.操作Clickhouse可能会遇到的问题

6.1 遇到异常:Too many simultaneous queries

原因:当前正在执行的查询数过多,超过了数据库的最大限制。 1)检查clickhouse设置的最大查询数:查看clickhouse的配置文件config.xml,其中的选项max_concurrent_queries,如下图: image.png 2)确认当前实际在运行的查询数量:

--查询正在执行中的操作(默认最大值为100)
select count(*) from system.processes

image.png 解决办法: 1)调大max_concurent_queries的参数值,但是这种情况治标不治本,需要分析为啥会有大量请求阻塞; 2)终止正在执行的查询:可以先看一下有哪些字段 image.png 然后根据具体条件进行kill操作: image.png

6.2 出现异常:Server is now in maintenance mode, only allow internal query

原因:服务端正在重启,不允许操作,只能通过命令行进行查询 image.png

6.3 出现异常:Table is in readonly mode

问题现象: image.png 原因:主要问题是clickhouse访问Zookeeper的令牌失效了,此问题和认证机制有关,当时得原因是:ClickHouseServer启动之后,开始和Zookeeper认证,认证之后24h过期。如果 CKzookeeper 之间的连接不断,就不会有问题,如果24h内断开,也可以重连成功,不会有问题。超过24h后,断开,就重连不成功了。这是组件的一个bug,后来针对安全认证进行了优化,会自动刷新令牌。

举报

相关推荐

0 条评论