ClickHouse的表引擎

ClickHouse的表引擎有很多,在这介绍一下常用的引擎。更多的表引擎请参考链接:https://clickhouse.yandex/docs/zh/operations/table_engines/表的引擎决定了以下条件:

  1. 数据的存储方式和位置,写到哪里以及从哪里读取数据
  2. 支持哪些查询以及如何支持
  3. 并发数据访问
  4. 如果索引存在的话,索引如何使用
  5. 是否可以执行多线程请求
  6. 数据复制参数

下面介绍一下常见的表引擎,重要的MergeTree引擎放到下一篇文章中讲解。

TinyLog

该引擎为日志引擎系列,最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入数据时,将数据附加到文件末尾。TinyLog引擎没有并发控制。也就是说如果同时从表中读取和写入数据,则读取数据将抛出异常;如果同时写入多个查询中的表,则数据将被破坏。
这种表的引擎适合一次写入,然后根据需要多次读取。该引擎适用于相对较小的表(建议最多1000000行)。如果有许多小表的话,使用此引擎是适合的,因为它需要打开的文件更少。当拥有大量小表是,可能会导致性能降低。不支持索引。我们来看下TinyLog引擎的案例,在执行之前先启动ClickHouse的服务

1
2
3
4
-- 在default库下执行
create table t_tiny_log(a UInt16, b String) ENGINE=TinyLog;
insert into t_tiny_log(a,b) values(1,'abc');
insert into t_tiny_log(a,b) values(2,'def');

ClickHouse的数据目录默认放在/var/lib/clickhouse/该目录的配置可以在config.xml中更改。我们可以查看一下该目录下的t_tiny_log表有哪些文件。cd /var/lib/clickhouse/data/default/t_tiny_log该目录下会有三个文件a.bin、b.bin、 sizes.json后缀名为bin是压缩过的对应的列的数据,sizes.json中记录了每个bin文件的大小。

Log

Log引擎同属于日志引擎系列。Log与TinyLog的不同之处在于,«标记» 的小文件与列文件存在一起。这些标记写在每个数据块上,并且包含偏移量,这些偏移量指示从哪里开始读取文件以便跳过指定的行数。这使得可以在多个线程中读取表数据。对于并发数据访问,可以同时执行读取操作,而写入操作则阻塞读取和其它写入。Log引擎不支持索引。同样,如果写入表失败,则该表将被破坏,并且从该表读取将返回错误。Log引擎适用于临时数据,write-once表以及测试或演示目的。创建表的语法和TinyLog语法一致,将ENGINE=Log

StripeLog

该引擎同样属于日志引擎系列。写入许多小数据量(小于一百万行)的表场景下使用这个引擎。StripeLog引擎不支持ALTER UPDATE和ALTER DELETE操作。

在写数据时StripeLog引擎将所有列存储在一个文件中。每一次insert请求,ClickHouse将数据块追加在表文件的末尾,逐列写入。ClickHouse为每张表写入以下文件:

  1. data.bin 数据文件
  2. index.mrk 带标记的文件,标记包含了已插入的每个数据块中每列的偏移量

在读数据时带标记的文件可以使ClickHouse并行的读取数据。也就是select请求返回行的顺序是不可预测的。可以使用order by子句对其进行排序。可以看下StripeLog表引擎的案例

1
2
3
create table stripe_log_table(timestamp DateTime,message_type String,message String)ENGINE=StripeLog;
INSERT INTO stripe_log_table VALUES (now(),'REGULAR','The first regular message');
INSERT INTO stripe_log_table VALUES (now(),'REGULAR','The second regular message'),(now(),'WARNING','The first warning message');

在案例中使用两次insert请求从而在data.bin文件中创建两个数据块。ClickHouse在查询数据时使用多线程,每个线程读取单独的数据块并在完成后独立的返回结果行。所以在查询时会输出和输入时相应块的顺序是不同的。如下所示

1
2
3
-- 以下两个查询语句返回块是不一致的
select * from stripe_log_table;
select * from stripe_log_table order by timestamp;

Memory

内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。简单的查询会有非常高的性能。一般用的地方不多,除了用来测试,就是在需要非常高的性能,同时数据量又不太大(上限大概1亿行)的场景。

Merge

Merge引擎本身不存储数据,但可用于同时从任意多个其他的表中读取数据。读是自动并行的,不支持写入。读取时,那些被真正读取到的数据的表的索引(如果有的话)会被使用。Merge引擎的参数:一个数据库名和一个用于匹配表名的正则表达式。来看下案例

1
2
3
4
5
6
7
8
9
10
-- 先创建t1、t2、t3三个表,并插入数据
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');
-- 用Merge引擎的t表再把他们链接起来
create table t(id UInt16,name String)ENGINE=Merge(currentDatabase(),'^t');
select *from t;