CH拥有非常庞大的表引擎体系,截至目前共拥有合并树、外部存储、内存、文件、接口和其他6大类20多种表引擎。
在生产环境的绝大部分场景中,都会使用MergeTree系列的表引擎。因为只有合并树系列的表引擎才支持主键索引、数据分区、数据副本和数据采样这些特性,同时也只有此系列的表引擎支持ALTER相关操作。
表引擎(即表的类型)决定了:
-
数据的存储方式和位置,写到哪里以及从哪里读取数据
-
支持哪些查询以及如何支持。
-
并发数据访问。
-
索引的使用(如果存在)。
-
是否可以执行多线程请求。
-
数据复制参数,是否可以存储数据副本。
注意:引擎的名称大小写敏感
5.1 Log系列引擎
Log家族具有最小功能的轻量级引擎。当您需要快速写入许多小表(最多约100万行)并在以后整体读取它们时,该类型的引擎是最有效的。
5.1.1 TinyLog引擎(数据不分块)
最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入时,数据将附加到文件末尾。
该引擎没有并发控制
-
只支持并发读
-
如果同时从表中读取和写入数据,则读取操作将抛出异常
-
如果同时写入多个查询中的表,则数据将被破坏
演示
-
建表
create table tb_tinylog(id Int8 , name String , age Int8) engine=TinyLog ;
-
插入数据
insert into tb_tinylog values(1,'zhangsan',56),(2,'lisi',55),(3,'wangwu',123) ;
列名.bin 是压缩过的对应的列的数据,sizes.json 中记录了每个 *.bin 文件的大小;
这种表引擎的典型用法是 write-once:首先只写入一次数据,然后根据需要多次读取。此引擎适用于相对较小的表(建议最多1,000,000行)。如果有许多小表,则使用此表引擎是适合的,因为它比需要打开的文件更少。当拥有大量小表时,可能会导致性能低下。不支持索引。
5.1.2 StripeLog(数据分块列在一起)
在你需要写入许多小数据量(小于一百万行)的表的场景下使用这个引擎。
-
建表
CREATE TABLE stripe_log_table( timestamp DateTime, message_type String, message String)ENGINE = StripeLog ;
-
插入数据
INSERT INTO stripe_log_table VALUES (now(),'Title','大数据') ; INSERT INTO stripe_log_table VALUES (now(),'Subject','大数据'),(now(),'WARNING','Python+大数据开发') ;
-
写数据
StripeLog 引擎将所有列存储在一个文件中。对每一次 Insert 请求,CH 将数据块追加在表文件的末尾,逐列写入。
CH 为每张表写入以下文件:
data.bin — 数据文件。
index.mrk — 带标记的文件。标记包含了已插入的每个数据块中每列的偏移量。
StripeLog 引擎不支持 ALTER UPDATE 和 ALTER DELETE 操作。
-
读数据
带标记的文件使得 CH 可以并行的读取数据。这意味着 SELECT 请求返回行的顺序是不可预测的。需使用 ORDER BY 子句对行进行排序。
5.1.3 Log(数据分块记录偏移量)
日志与 TinyLog 的不同之处在于,«标记» 的小文件与列文件存在一起。这些标记写在每个数据块上,并且包含偏移量,这些偏移量指示从哪里开始读取文件以便跳过指定的行数。这使得可以在多个线程中读取表数据。对于并发数据访问,可以同时执行读取操作,而写入操作则阻塞读取和其它写入。Log 引擎不支持索引。同样,如果写入表失败,则该表将被破坏,并且从该表读取将返回错误。Log 引擎适用于临时数据,write-once 表以及测试或演示目的。
-
建表
create table tb_log(id Int8 , name String , age Int8) engine=Log ;
-
插入数据
insert into tb_log values(1,'zhangsan',56),(2,'lisi',55),(3,'wangwu',123) ; insert into tb_log values(4,'zhaoliu',26),(5,'tianqi',35);
5.1.4 总结
-
共同属性
数据存储在磁盘上 写入时将数据追加在文件末尾 不支持突变(Mutation)操作 不支持索引(这意味着
SELECT
在范围查询时效率不高) 非原子地写入数据(如果某些事情破坏了写操作,例如服务器的异常关闭,你将会得到一张包含了损坏数据的表)
Log 和 StripeLog 引擎支持:
-
并发访问数据的锁。
INSERT
请求执行过程中表会被锁定,并且其他的读写数据的请求都会等待直到锁定被解除。如果没有写数据的请求,任意数量的读请求都可以并发执行。 -
并行读取数据。
在读取数据时,CH使用多线程。每个线程处理不同的数据块。
Log引擎为表中的每一列使用不同的文件。StripeLog将所有的数据存储在一个文件中。因此 StripeLog引擎在操作系统中使用更少的描述符,但是Log引擎提供更高的读性能。
TinyLog引擎是该系列中最简单的引擎并且提供了最少的功能和最低的性能。TingLog引擎不支持并行读取和并发数据访问,并将每一列存储在不同的文件中。它比其余两种支持并行读取的引擎的读取速度更慢,并且使用了和 Log 引擎同样多的描述符。你可以在简单的低负载的情景下使用它。
5.2 MergeTree家族引擎
MergeTree系列的表引擎提供了用于弹性和高性能数据检索的大多数功能:列存储,自定义分区,稀疏的主索引,辅助数据跳过索引等。
除了基础表引擎MergeTree之外,常用的表引擎还有
-
ReplacingMergeTree
-
SummingMergeTree
-
AggregatingMergeTree
-
CollapsingMergeTree
-
VersionedCollapsingMergeTree
每一种合并树的变种,在继承了基础MergeTree的能力之后,又增加了独有的特性。其名称中的“合并” 二字奠定了所有类型MergeTree的基因,它们的所有特殊逻辑,都是在触发合并的过程中被激活的。
对于生产用途,ReplicatedMergeTree是必经之路,因为它为常规MergeTree引擎的所有功能增加了高可用性。一个额外的好处是在数据提取时自动进行重复数据删除,因此如果插入过程中出现网络问题,该软件可以安全地重试。
MergeTree引擎的主要缺点是它们很重。如果您需要许多小表(例如用于临时数据),请考虑使用Log engine family。
-
允许快速写入不断变化的对象状态。
-
删除后台中的旧对象状态。这显着降低了存储体积。
该MergeTree系列的引擎和其他引擎是最强大的CH表引擎。
该MergeTree系列中的引擎旨在将大量数据插入表中。数据快速地逐个部分地写入表中,然后应用规则在后台合并这些部分。这种方法比插入期间连续重写存储中的数据效率更高。
主要特点:
-
存储按主键排序的数据。
可以创建一个小的稀疏索引,更快地查找数据。
-
如果指定了分区键,则可以使用分区。
ClickHouse支持的某些分区操作比对相同数据,相同结果比常规操作更有效。ClickHouse还会自动切断在查询中指定了分区键的分区数据。这也提高了查询性能。
-
数据复制支持。
ReplicatedMergeTree表族提供数据复制。
-
数据采样支持。
可以在表中设置数据采样方法。
5.2.1 MergeTree
MergeTree在写入一批数据时,数据总会以数据片段的形式写入磁盘,且数据片段不可修改。为了避免片段过多,CH会通过后台线程,定期合并这些数据片段,属于相同分区的数据片段会被合成一个新的片段。
5.2.1.1 基本语法格式
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() ORDER BY expr[PARTITION BY expr] [PRIMARY KEY expr] [SAMPLE BY expr] [TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...] [SETTINGS name=value, ...]
-
参数解读
-
PARTITION BY [*选填*]:分区键,用于指定表数据以何种标准进行分区。分区键既可以是单个列字段,也可以通过元组的形式使用多个列字段,同时它也支持使用列表达式。如果不声明分区键,则CH会生成一个名为all的分区。
-
ORDER BY [*必填*]:排序键,用于指定在一个数据片段内,数据以何种标准排序。默认情况下主键(PRIMARY KEY)与排序键相同。排序键既可以是单个列字段,也可以通过元组的形式使用多个列字段,例如ORDER BY(CounterID,EventDate)。
-
PRIMARY KEY [*选填*]:主键,顾名思义,声明后会依照主键字段生成一级索引,用于加速表查询。默认情况下,主键与排序键 (ORDER BY)相同,所以通常直接使用ORDER BY代为指定主键,无须刻意通过PRIMARY KEY声明。所以在一般情况下,在单个数据片段内,数据与一级索引以相同的规则升序排列。与其他数据库不同,MergeTree主键允许存在重复数据(*ReplacingMergeTree可以去重*)。
-
SAMPLE BY [*选填*]:抽样表达式,用于声明数据以何种标准进行采样。如果使用了此配置项,那么在主键的配置中也需要声明同样的表达式,例如:
ORDER BY (CounterID, EventDate, intHash32(UserID) SAMPLE BY intHash32(UserID)
-
SETTINGS:index_granularity [*选填*]: index_granularity对于MergeTree而言是一项非常重要的参数,它表示索引的粒度,默认值为8192。其具体声明方式如下所示:
SETTINGS index_granularity = 8192;
-
SETTINGS:index_granularity_bytes [*选填*]:在19.11版本之前,CH只支持固定大小的索引间隔,由 index_granularity控制,默认为8192。在新版本中,它增加了自适应间隔大小的特性,即根据每一批次写入数据的体量大小,动态划分间隔大小。而数据的体量大小,正是由index_granularity_bytes参数控制的,默认为10M(10×1024×1024),设置为0表示不启动自适应功能。
-
SETTINGS:enable_mixed_granularity_parts [*选填*]:设置是否开启自适应索引间隔的功能,默认开启。
-
SETTINGSÿ
-