ClickHouse表引擎

1 概述

​ 表引擎在ClickHouse中的作用十分关键,表引擎有如下作用:

​ (1)数据如何存储,存在哪,数据写到哪, 怎样读取数据;

​ (2)支持哪些查询以及如何支持;

​ (3)并发数据访问;

​ (4)索引的使用;

​ (5)是否可以执行多线程的请求;

​ (6)数据如何同步。

2 表引擎系列

2.1 MergeTree系列

​ MergeTree系列是对于高负载任务的最通用和最实用的表引擎。这些引擎共享的属性是快速数据插入和后续的后台数据处理。想要高效地一批批写入数据片段,并希望这些数据片段在后台按照一定规则合并。相比在插入时不断修改(重写)数据进存储,这种策略会高效很多。MergeTree系列引擎支持数据复制、分区和其他引擎不支持的特性。有如下特点:①数据按照主键进行排序;②可以使用分区(如果指定了主键);③支持数据副本;④支持数据采样

2.1.1 MergeTree

​ MergeTree 引擎支持索引,通过主键和日期来构建索引, 同时提供 数据的实时更新能力. 这是目前 ClickHouse处理能力最好的引擎。

​ 注意:①不能和Merge 引擎相混淆。

​ ②MergeTree虽然有主键索引,但是其主要作用是加速查询,而不是类似MySQL等数据库用来保持记录唯一。即便在Compaction完成后,主键相同的数据行也仍旧共同存在。

​ 建表语句如下

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
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]

​ 参数说明:

参数 说明
ENGINE = MergeTree() ENGINE:引擎名和参数
PARTITION BY expr PARTITION BY:分区键,按月分区采用“YYYYMM”格式,可以使用toYYYYMM (date_column)表达式,date_column类型必须是Date
ORDER BY expr ORDER BY:排序键,列的元组或任意表达式 (字段的组合), 或者单独的表达式。如:ORDER BY (CounterID, EventDate)
PRIMARY KEY expr PRIMARY KEY:主键,需要与排序键字段不同,默认主键与排序键相同
SAMPLE BY expr SAMPLE BY:抽样表达式,要用抽样表达式,主键必须包含这个表达式
TTL expr [DELETE |TO DISK ‘xxx’ TO VOLUME ‘xxx’], … TTL:指定行存储时间和定义磁盘与卷之间自动部件移动逻辑的规则列表。如:TTL create_time + INTERVAL 1 MONTH(表数据的生命周期为期一个月 )它的含义是当ClickHouse合并数据分区时, 会根据create_time这一列的时间数据以及之后一个月的这样一周期内的数据进行保存,不在这一时间段内的数据,ck就是主动删除分区目录下的列文件
SETTINGS name=value, … SETTINGS:影响MergeTree性能的额外参数
①index_granularity:索引粒度,索引键相邻标记间的数据行数,默认8192
②use_minimalistic_part_header_in_zookeeper:在Zookeeper中的存储方式
③min_merge_bytes_to_use_direct_io:使用直接I/O来操作磁盘的合并操作时的最小数据量

​ 测试:test_mt表的主键为(id, create_time),并且按照主键进行存储排序,按照create_time进行数据分区,根据create_time这一列的时间数据保留最近一个月。

CREATE TABLE test_mt ( \
      id UInt16, \
      create_time Date, \
      comment Nullable(String) \
) ENGINE = MergeTree() \
PARTITION BY create_time \
ORDER BY  (id, create_time) \
PRIMARY KEY (id, create_time) \
TTL create_time + INTERVAL 1 MONTH \
SETTINGS index_granularity=8192;

​ 插入数据

insert into test_mt values(0, '2020-01-01', null);
insert into test_mt values(0, '2020-01-01', null);
insert into test_mt values(1, '2020-01-02', null);
insert into test_mt values(2, '2020-01-03', null);

​ 查询数据

SELECT count(*) 
FROM test_mt 
┌─count()─┐
│       4 │
└─────────┘


SELECT *
FROM test_mt 

┌─id─┬─create_time─┬─comment─┐
│  2 │  2020-01-03 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  1 │  2020-01-02 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  0 │  2020-01-01 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  0 │  2020-01-01 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘

​ 可以发现虽然主键id、create_time相同的数据只有3条数据,但是结果却有4行。因为MergeTree采用类似LSM tree的结构,很多存储层处理逻辑直到Compaction期间才会发生。因此强制后台compaction执行

optimize table test_mt final;

​ 再次查询,发现没有数据了。

SELECT count(*)
FROM test_mt 

┌─count()─┐
│       0 │
└─────────┘

​ 是因为TTL的原因,我们在表上加了TTL当表内的数据过期时, ClickHouse会删除所有对应的行。如果是列上加TTL,当列字段中的值过期时, ClickHouse会将它们替换成数据类型的默认值。如果分区内,某一列的所有值均已过期,则ClickHouse会从文件系统中删除这个分区目录下的列文件。

​ 如果没有加TTL的查询出来应该是如下所示

SELECT count(*)
FROM test_mt 

┌─count()─┐
│       4 │
└─────────┘

select * from test_mt;
┌─id─┬─create_time─┬─comment─┐
│  2 │  2020-01-03 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  1 │  2020-01-02 │ ᴺᵁᴸᴸ    │  
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  0 │  2020-01-01 │ ᴺᵁᴸᴸ    │
│  0 │  2020-01-01 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘

2.1.2 ReplacingMergeTree

​ 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]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]

​ 参数说明:ENGINE = ReplacingMergeTree([ver]),这个ver是版本列,类型是UInt*,Date或者DateTime,合并的时候ReplacingMergeTree从具有相同主键的行中选择一行留下,如果ver列没有指定,选择最后一条,如果ver列已指定,选择ver最大的版本。其他的参考MergeTree的

​ 测试:

CREATE TABLE test_rmt (\
      id UInt16,\
      create_time Date,\
      comment Nullable(String)\
) ENGINE = ReplacingMergeTree()\
	PARTITION BY create_time\
	ORDER BY  (id, create_time)\
	PRIMARY KEY (id, create_time)\
	TTL create_time + INTERVAL 1 MONTH\
	SETTINGS index_granularity=8192;

​ 插入数据:

insert into test_rmt values(0, '2020-05-01', null);
insert into test_rmt values(0, '2020-05-01', null);
insert into test_rmt values(1, '2020-05-02', null);
insert into test_rmt values(2, '2020-05-03', null);

​ 查询结果:

SELECT count(*)
FROM test_rmt 

┌─count()─┐
│       4 │
└─────────┘


SELECT *
FROM test_rmt 

┌─id─┬─create_time─┬─comment─┐
│  1 │  2020-05-02 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  2 │  2020-05-03 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  0 │  2020-05-01 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  0 │  2020-05-01 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘


​ 可以发现数据还是4条,强制后台compaction

optimize table test_rmt final;

​ 再次查询

SELECT count(*)
FROM test_rmt 

┌─count()─┐
│       3 │
└─────────┘


SELECT *
FROM test_rmt 

┌─id─┬─create_time─┬─comment─┐
│  1 │  2020-05-02 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  2 │  2020-05-03 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘
┌─id─┬─create_time─┬─comment─┐
│  0 │  2020-05-01 │ ᴺᵁᴸᴸ    │
└────┴─────────────┴─────────┘


​ 总结:虽然ReplacingMergeTree提供了主键去重的能力,但是仍旧有以下缺点:①在没有彻底optimize之前,可能无法达到主键去重的效果,部分数据已经去重,部分没有去重②在分布式情况下,相同primary key的数据可能被sharding到不同节点上,不同shard间可能无法去重③无法预测optimize具体执行时间点④海量数据下要手动执行optimize需要消耗大量时间,无法满足业务即时查询的需求

2.1.3 SummingMergeTree

​ SummingMergeTree与MergeTree不区别在与当合并SummingMergeTree表的数据片段时,会把相同主键的行合并为一行,这一行包含了被合并的行中具有数值数据类型的列的汇总值,对于不可加的列会取出一个最先出现的值。如果相同的主键对应大量的行,可以显著减少存储空间并加快数据查询的速度。

​ 语法:

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, ...]

​ 参数说明:ENGINE = SummingMergeTree([columns]):[columns]表示将要被汇总的列的列名的元组。

​ 测试:

CREATE TABLE test_smt(\
    date Date,\
    name String,\
    money UInt16,\
    not_sum UInt16\
)\
ENGINE = SummingMergeTree(money)\
PARTITION by date \
ORDER by (date,name);

​ 插入数据:

insert into test_smt values ('2020-05-01', 'zs', 6, 1);
insert into test_smt values ('2020-05-01', 'ls', 8, 2);
insert into test_smt values ('2020-05-02', 'ww', 6, 3);
insert into test_smt values ('2020-05-02', 'ww', 8, 4);
insert into test_smt values ('2020-05-03', 'zl', 6, 5);

​ 查询:

SELECT *
FROM test_smt 

┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-02 │ ww   │     8 │       4 │
└────────────┴──────┴───────┴─────────┘
┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-01 │ zs   │     6 │       1 │
└────────────┴──────┴───────┴─────────┘
┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-03 │ zl   │     6 │       5 │
└────────────┴──────┴───────┴─────────┘
┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-01 │ ls   │     8 │       2 │
└────────────┴──────┴───────┴─────────┘
┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-02 │ ww   │     6 │       3 │
└────────────┴──────┴───────┴─────────┘



​ 通过GROUP BY进行聚合查询

SELECT 
    date, 
    name, 
    sum(money), 
    min(not_sum)
FROM test_smt 
GROUP BY 
    date, 
    name

┌───────date─┬─name─┬─sum(money)─┬─min(not_sum)─┐
│ 2020-05-01 │ ls   │          8 │            2 │
│ 2020-05-02 │ ww   │         14 │            3 │
│ 2020-05-03 │ zl   │          6 │            5 │
│ 2020-05-01 │ zs   │          6 │            1 │
└────────────┴──────┴────────────┴──────────────┘


​ 强制compaction

optimize table test_smt final;

​ 再次查询

SELECT *
FROM test_smt 

┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-03 │ zl   │     6 │       5 │
└────────────┴──────┴───────┴─────────┘
┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-01 │ ls   │     8 │       2 │
│ 2020-05-01 │ zs   │     6 │       1 │
└────────────┴──────┴───────┴─────────┘
┌───────date─┬─name─┬─money─┬─not_sum─┐
│ 2020-05-02 │ ww   │    14 │       3 │
└────────────┴──────┴───────┴─────────┘

2.1.4 ClollapsingMergeTree

​ ClollapsingMergeTree实现了对ReplacingMergeTree功能的限制,在建表语句中指定一个标记列sign,后台Compaction时会将主键相同,sign相反的行进行删除。ClollapsingMergeTree将行安装sign的值分为两类:sign=1为状态行,sign=-1位取消行。每次需要新增状态时,写入一行状态行,需要删除状态时,写入一行取消行。后台在Compaction时,状态行与取消行会自动做折叠(删除),而尚未进行Compaction的数据,状态行与取消行同时存在。

​ 为了能够达到主键折叠(删除)的目的,对业务层进行适当改造:①执行删除操作需要写入取消行,而取消行中需要包含与原始状态行主键一样的数据(Sign列除外)。所以在应用层需要记录原始状态行的值,或者在执行删除操作前先查询数据库获取原始状态行。②由于后台Compaction时机无法预测,在发起查询时,状态行和取消行可能尚未被折叠;另外,ClickHouse无法保证primary key相同的行落在同一个节点上,不在同一节点上的数据无法折叠。因此在进行count(*)、sum(col)等聚合计算时,可能会存在数据冗余的情况。为了获得正确结果,业务层需要改写SQL,将count()、sum(col)分别改写为sum(Sign)、sum(col * Sign)。

​ 测试:

CREATE TABLE test_cmt1(\
    UserID UInt64,\
    PageViews UInt8,\
    Duration UInt8,\
    Sign Int8\
)\
ENGINE = CollapsingMergeTree(Sign)\
ORDER BY UserID;

​ 插入状态行,sign列的值为1

INSERT INTO test_cmt1 VALUES (123456, 6, 88, 1);

​ 插入一行取消行,用于抵消上述状态行。sign列的值为-1,其余值与状态行一致;并且插入一行主键相同的新状态行,用来将PageViews从6更新至7,将Duration从888更新为889.

INSERT INTO test_cmt1 VALUES (123456, 6, 88, -1), (123456, 7, 90, 1);

​ 查询数据:可以看到未Compaction之前,状态行与取消行共存。

SELECT *
FROM test_cmt1 

┌─UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 123456 │         6 │       88 │    1 │
└────────┴───────────┴──────────┴──────┘
┌─UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 123456 │         6 │       88 │   -1 │
│ 123456 │         7 │       90 │    1 │
└────────┴───────────┴──────────┴──────┘


	为了获取正确的sum值,需要改写SQL: sum(PageViews) => sum(PageViews * Sign)、 sum(Duration) => sum(Duration * Sign)
SELECT 
    UserID, 
    sum(PageViews * Sign) AS PageViews, 
    sum(Duration * Sign) AS Duration
FROM test_cmt1 
GROUP BY UserID
HAVING sum(Sign) > 0

┌─UserID─┬─PageViews─┬─Duration─┐
│ 123456 │         7 │       90 │
└────────┴───────────┴──────────┘


​ 强制后台Compaction

optimize table test_cmt1 final;

​ 再次查询,可以看到状态行、取消行已经被折叠,只剩下最新的一行状态行。

SELECT *
FROM test_cmt1 

┌─UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 123456 │         7 │       90 │    1 │
└────────┴───────────┴──────────┴──────┘


​ CollapsingMergeTree虽然解决了主键相同的数据即时删除的问题,但是状态持续变化且多线程并行写入情况下,状态行与取消行位置可能乱序,导致无法正常折叠。

CREATE TABLE test_cmt2(\
    UserID UInt64,\
    PageViews UInt8,\
    Duration UInt8,\
    Sign Int8\
)\
ENGINE = CollapsingMergeTree(Sign)\
ORDER BY UserID;


​ 先插入取消行,然后后插入状态行

INSERT INTO test_cmt2 VALUES (123456, 6, 88, -1);
INSERT INTO test_cmt2 VALUES (123456, 6, 88, 1);

​ 强制Compaction

optimize table test_cmt2 final;

​ 查询

select * from test_cmt2;

​ 可以看到即便Compaction之后也无法进行主键折叠: 2行数据仍旧都存在。

SELECT *
FROM test_cmt2 

┌─UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 123456 │         6 │       88 │   -1 │
│ 123456 │         6 │       88 │    1 │
└────────┴───────────┴──────────┴──────┘

​ 为了解决CollapsingMergeTree乱序写入情况下无法正常折叠问题,VersionedCollapsingMergeTree表引擎在建表语句中新增了一列Version,用于在乱序情况下记录状态行与取消行的对应关系。主键相同,且Version相同、Sign相反的行,在Compaction时会被删除。

​ 与CollapsingMergeTree类似, 为了获得正确结果,业务层需要改写SQL,将count()、sum(col)分别改写为sum(Sign)、sum(col * Sign)。

2.1.5 AggregatingMergeTree

​ AggregatingMergeTree与MergeTree的区别在于会进行预先的聚合,用于提升聚合计算的性能。与SummingMergeTree的区别在于SummingMergeTree对于非主键列进行sum聚合,而AggregatingMergeTree则可以指定各种聚合函数。

​ AggregatingMergeTree需要结合物化视图或者ClickHouse的特殊数据类型ArrregateFunction一起使用,在insert写入的时候需要使用-State语法,在select查询的时候使用-Merge语法。

​ 语法:

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = AggregatingMergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[TTL expr]
[SETTINGS name=value, ...]

​ (1)结合物化视图

CREATE TABLE test_amt1(\
    UserID UInt64,\
    CounterID UInt8,\
    StartDate Date,\
    Sign Int8\
)\
ENGINE = CollapsingMergeTree(Sign)\
ORDER BY UserID;

​ 对test_amt建立物化视图,进行预先聚合。预先聚合使用的函数分别为: sumState, uniqState。对应于写入语法-State.

CREATE MATERIALIZED VIEW test_amt1_view\
ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(StartDate) ORDER BY (CounterID, StartDate)\
AS SELECT\
    CounterID,\
    StartDate,\
    sumState(Sign)    AS Visits,\
    uniqState(UserID) AS Users\
FROM test_amt1\
GROUP BY CounterID, StartDate;

​ 插入数据

INSERT INTO test_amt1 VALUES(123, 0, '2020-05-01', 3);
INSERT INTO test_amt1 VALUES(123, 1, '2020-05-01', 3);
INSERT INTO test_amt1 VALUES(111, 2, '2020-05-02', 2);

​ 对物化视图进行最终的聚合操作。使用的聚合函数为 sumMerge, uniqMerge。

SELECT \
    StartDate, \
    sumMerge(Visits) AS Visits, \
    uniqMerge(Users) AS Users\
FROM test_amt1_view \
GROUP BY StartDate\
ORDER BY StartDate ASC

┌──StartDate─┬─Visits─┬─Users─┐
│ 2020-05-01 │      6 │     1 │
│ 2020-05-02 │      2 │     1 │
└────────────┴────────┴───────┘

​ 普通函数 sum, uniq不再可以使用,会报错: Illegal type AggregateFunction(sum, Int8) of argument

​ (2)配合特殊数据类型AggregateFunction使用

CREATE TABLE test_amt2(\
    CounterID UInt8,\
    StartDate Date,\
    Money UInt64,\
    UserID UInt64\
) ENGINE = MergeTree() \
PARTITION BY toYYYYMM(StartDate) \
ORDER BY (CounterID, StartDate);

​ 插入数据

INSERT INTO test_amt2 VALUES(111, '2020-05-01', 10, 1);
INSERT INTO test_amt2 VALUES(111, '2020-05-01', 12, 5);
INSERT INTO test_amt2 VALUES(122, '2020-05-02', 9, 2);

​ 建立预先聚合表,其中UserID一列的类型为:AggregateFunction(uniq, UInt64)

CREATE TABLE test_amt2_agg(\
    CounterID UInt8,\
    StartDate Date,\
    Money AggregateFunction(sum, UInt64),\
    UserID AggregateFunction(uniq, UInt64)\
) ENGINE = AggregatingMergeTree() \
PARTITION BY toYYYYMM(StartDate) \
ORDER BY (CounterID, StartDate);

​ 从明细表中读取数据,插入聚合表,子查询中使用的聚合函数为 uniqState

INSERT INTO test_amt2_agg SELECT \
    CounterID, \
    StartDate, \
    sumState(Money),\
    uniqState(UserID)\
FROM test_amt2 \
GROUP BY \
    CounterID, \
    StartDate

​ 注意:不能使用普通insert语句向AggregatingMergeTree中插入数据。会报错:Cannot convert UInt64 to AggregateFunction(uniq, UInt64)

​ 从聚合表中查询,select中使用的聚合函数为uniqMerge

SELECT CounterID,StartDate,sumMerge(Money),uniqMerge(UserID) AS state\
FROM test_amt2_agg \
GROUP BY \
    CounterID, \
    StartDate

SELECT 
    CounterID, 
    StartDate, 
    sumMerge(Money), 
    uniqMerge(UserID) AS state
FROM test_amt2_agg 
GROUP BY 
    CounterID, 
    StartDate

┌─CounterID─┬──StartDate─┬─sumMerge(Money)─┬─state─┐
│       122 │ 2020-05-02 │               9 │     1 │
│       111 │ 2020-05-01 │              22 │     2 │
└───────────┴────────────┴─────────────────┴───────┘


2.2 Log系列

​ Log系列表引擎功能相对简单,轻量级引擎主要用于快速写入小表(1百万行左右的表),然后全部读出的场景。

​ 特点:

​ (1)数据被顺序append写到磁盘上。

​ (2)不支持delete、update。

​ (3)不支持index。

​ (4)不支持原子性写。

​ (5)insert会阻塞select操作。

​ TinyLog,StripLog,Log区别如下:①TinyLog:不支持并发读取数据文件,查询性能较差;格式简单,适合用来暂存中间数据。②StripLog:支持并发读取数据文件,查询性能比TinyLog好;将所有列存储在同一个大文件中,减少了文件个数。③Log:支持并发读取数据文件,查询性能比TinyLog好;每个列会单独存储在一个独立文件中。

2.2.1 TinyLog

​ 最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入时,数据将附加到文件末尾。该引擎没有并发控制,如果同时从表中读取和写入数据,则读取操作将抛出异常;如果同时写入多个查询中的表,则数据将被破坏。
不支持索引。

​ 测试:

CREATE TABLE test_tl (\
	id UInt16,\
	name String)\
ENGINE=TinyLog;

​ 插入数据:

INSERT INTO test_tl (id, name) values (1, 'zs');

​ 进入ClickHouse的test_tl表的数据存储目录

[root@ambari01 test_tl]# cd /data/clickhouse/data/test/test_tl

[root@ambari01 test_tl]# ll
total 12
-rw-r----- 1 clickhouse hadoop 28 May 22 18:07 id.bin
-rw-r----- 1 clickhouse hadoop 29 May 22 18:07 name.bin
-rw-r----- 1 clickhouse hadoop 64 May 22 18:07 sizes.json


​ id.bin和name.bin是压缩过的对应的列的数据,sizes.json 中记录了每个 *.bin 文件的大小。

[root@ambari01 test_tl]# cat sizes.json
{"yandex":{"id%2Ebin":{"size":"28"},"name%2Ebin":{"size":"29"}}}

2.3 Integration系列

​ 该系统表引擎主要用于将外部数据导入到ClickHouse中,或者在ClickHouse中直接操作外部数据源。

2.3.1 Kafka

​ 将Kafka Topic中的数据直接导入到ClickHouse。

2.3.2 MySQL

​ 将Mysql作为存储引擎,直接在ClickHouse中对MySQL表进行select等操作。

2.3.3 JDBC/ODBC

​ 通过指定jdbc、odbc连接串读取数据源。

2.3.4 HDFS

​ 直接读取HDFS上的特定格式的数据文件;

2.4 Special系列

​ Special系列的表引擎,是为了特定场景而定制的,不做详述。

​ Memory:将数据存储在内存中,重启后会导致数据丢失。查询性能极好,适合于对于数据持久性没有要求的1亿一下的小表。在ClickHouse中,通常用来做临时表。

​ Buffer:为目标表设置一个内存buffer,当buffer达到了一定条件之后会flush到磁盘。

​ File:直接将本地文件作为数据存储。

​ Null:写入数据被丢弃、读取数据为空。

你可能感兴趣的:(ClickHouse)