0
点赞
收藏
分享

微信扫一扫

ClickHouse第四讲-表引擎

静守幸福 2022-03-11 阅读 114

MySQL 的数据表有 InnoDB MyISAM 存储引擎,不同的存储引擎提供不同的存储机

制、索引方式等功能,也可以称之为表类型。在 ClickHouse 中也有表引擎。

表引擎在 ClickHouse 中的作用十分关键,直接决定了数据如何存储和读取、是否支

持并发读写、是否支持 index 索引、支持的 query 种类、是否支持主备复制等。

ClickHouse 提供了大约 28 种表引擎,各有各的用途,比如有 Log 系列用来做小表

数据分析,MergeTree 系列用来做大数据量分析,而 Integration 系列则多用于外表数

据集成。再考虑复制表 Replicated 系列,分布式表 Distributed

ClickHouse 表引擎一共分为四个系列,分别是 Log 系列、MergeTree 系列、Integration 系列、Special 系列。其中包含了两种特殊的表引擎 Replicated、Distributed,功能上与其他表引擎正交,根据场景组合使用。

Log系列表引擎

Log 系列表引擎功能相对简单,主要用于快速写入小表(1 百万行左右的表),然后全

部读出的场景,即一次写入,多次查询。Log 系列表引擎包含:TinyLogStripeLog

Log 三种引擎。

几种 Log 表引擎的共性是:

数据被顺序 append 写到本地磁盘上。

不支持 deleteupdate 修改数据。

不支持 index(索引)。

不支持原子性写。如果某些操作(异常的服务器关闭)中断了写操作,则可能会获

得带有损坏数据的表。

insert 会阻塞 select 操作。当向表中写入数据时,针对这张表的查询会被阻塞,

直至写入动作结束。

它们彼此之间的区别是:

TinyLog:不支持并发读取数据文件,查询性能较差;格式简单,适合用来暂存

中间数据。

StripLog:支持并发读取数据文件,查询性能比 TinyLog 好;将所有列存储在

同一个大文件中,减少了文件个数。

Log:支持并发读取数据文件,查询性能比 TinyLog 好;每个列会单独存储在一

个独立文件中

TinyLog

TinyLog Log 系列引擎中功能简单、性能较低的引擎。

它的存储结构由数据文件和元数据两部分组成。其中,数据文件是按列独立存储的,也

就是说每一个列字段都对应一个文件。 由于 TinyLog 数据存储不分块,所以不支持并发数据读取,该引擎适合一次写入,多

次读取的场景,对于处理小批量中间表的数据可以使用该引擎,这种引擎会有大量小文件,

性能会低

create table t_tinylog(id UInt8,name String,age UInt8)  engine=TinyLog;

insert into t_tinylog values (1,'张三',18),(2,'李四',19),(3,' 王五',20);

#在表中删除一条数据,这里是不支持 delete

alter table t_tinylog delete where id = 1;

当 在 newdb 库 中 创 建 表 t_tinylog 后 , 在 ClickHouse 保 存 数 据 的 目 录 /var/lib/clickhouse/data/newdb/下会多一个 t_tinylog 目录在向表 t_tinylog 中插入数据后,进入“

t_tinylog”目录,查看目录下的文件, 如下图所示:

t_tinylog 中的每个列都单独对应一个*.bin 文件,同时还有一 个 sizes.json 文件存储元数据,记录了每个 bin 文件中数据大小

StripeLog

StripeLog 数据存储会划分块,每次插入对应一个数据块,拥

有更高的查询性能(拥有.mrk 标记文件,支持并行查询)。StripeLog 引擎将所有列存

储在一个文件中,使用了更少的文件描述符。对每一次 Insert 请求,ClickHouse

数据块追加在表文件的末尾,逐列写入。StripeLog 引擎不支持 ALTER UPDATE

ALTER DELETE 操作。

create table t_stripelog(id UInt8,name String,age UInt8) engine = StripeLog;

#向表 t_stripelog 中插入数据,这里插入分多次插入,会将数据插入不同的数据块中

node1 :) insert into t_stripelog values (1,'张三',18);

node1 :) insert into t_stripelog values (2,'李四',19);

当 在 newdb 库 中创 建表 t_stripelog 后 ,在 ClickHouse 保 存数 据的 目录 /var/lib/clickhouse/data/newdb/下会多一个 t_stripelog 目录,如图所示:

data.bin:数据文件,所有列字段都写入 data.bin 文件中。

index.mrk:数据标记文件,保存了数据在 data.bin 文件中的位置信息,即每

个插入数据列的 offset 信息,利用数据标记能够使用多个线程,并行度取

data.bin 压缩数据,提升查询性能。

sizes.json:元数据文件,记录了 data.bin index.mrk 大小信息。

Log

Log 引擎表适用于临时数据,一次性写入、测试场景。Log 引擎结合了 TinyLog 表引

擎和 StripeLog 表引擎的长处,是 Log 系列引擎中性能最高的表引擎。

Log 表引擎会将每一列都存在一个文件中,对于每一次的 INSERT 操作,会生成数据

,经测试,数据块个数与当前节点的 core 数一致。

create table t_log(id UInt8 ,name String ,age UInt8 ) engine = Log;

#向表 t_log 中插入数据,分多次插入,插入后数据存入数据块

node1 :) insert into t_log values (1,'张三',18);

node1 :) insert into t_log values (2,'李四',19);

node1 :) insert into t_log values (3,'王五',20);

node1 :) insert into t_log values (4,'马六',21);

node1 :) insert into t_log values (5,'田七',22);

当 在 newdb 库 中 创 建 表 t_log 后 , 在 ClickHouse 保 存 数 据 的 目 录

/var/lib/clickhouse/data/newdb/下会多一个 t_log 目录,如图所示:

我们发现表 t_log 中的每个列都对应一个*.bin 文件。其他两个文件的解释如下:

__marks.mrk:数据标记,保存了每个列文件中的数据位置信息,利用数据标记

能够使用多个线程,并行度取 data.bin 压缩数据,提升查询性能。

sizes.json:记录了*.bin __mark.mrk 大小的信息。

Special系列引擎

Memory 表引擎直接将数据保存在内存中,ClickHouse 中的 Memory 表引擎具有以

下特点:

Memory 引擎以未压缩的形式将数据存储在 RAM 中,数据完全以读取时获得的形

式存储。

并发数据访问是同步的,锁范围小,读写操作不会相互阻塞。

不支持索引。

查询是并行化的,在简单查询上达到最大速率(超过 10 GB /秒),在相对较少

的行(最多约 100,000,000)上有高性能的查询。

没有磁盘读取,不需要解压缩或反序列化数据,速度更快(在许多情况下,与

MergeTree 引擎的性能几乎一样高)。

重新启动服务器时,表存在,但是表中数据全部清空。

Memory 引擎多用于测试。

create table t_memory(id UInt8 ,name String, age UInt8) engine = Memory;

注意:”Memory”表引擎写法固定,不能小写。同时创建好表 t_memory 后,在对应

的磁盘目录/var/lib/clickhouse/data/newdb 下没有“

t_memory”目录,基于内存

存储,当重启 ClickHouse 服务后,表 t_memory 存在,但是表中数据全部清空。

Merge

Merge 引擎 (不要跟 MergeTree 引擎混淆) 本身不存储数据,但可用于同时从任

意多个其他的表中读取数据,这里需要多个表的结构相同,并且创建的 Merge 引擎表的结

构也需要和这些表结构相同才能读取。 读是自动并行的,不支持写入。读取时,那些被真正读取到数据的表如果设置了索引, 索引也会被使用。

Merge 引擎的参数:一个数据库名和一个用于匹配表名的正则表达式:

Merge(数据库, 正则表达式)

例如:Merge(hits, '^WatchLog') 表示数据会从 hits 数据库中表名匹配正则

^WatchLog’ 的表中读取。

注意:当选择需要读取的表时,会匹配正则表达式匹配上的表,如果当前 Merge 表的

名称也符合正则表达式匹配表名,这个 Merge 表本身会自动排除,以避免进入递归死循环,

当然也可以创建两个相互无限递归读取对方数据的 Merge 表,但这并没有什么意义。

例子:

create table m_t1 (id UInt8 ,name String,age UInt8) engine = TinyLog;

node1 :) insert into m_t1 values (1,'张三',18),(2,'李四',19)

#newdb 库中创建表 m_t2,并插入数据

node1 :) create table m_t2 (id UInt8 ,name String,age UInt8) engine = TinyLog;

node1 :) insert into m_t2 values (3,'王五',20),(4,'马六',21)

#newdb 库中创建表 m_t3,并插入数据

node1 :) create table m_t3 (id UInt8 ,name String,age UInt8) engine = TinyLog;

node1 :) insert into m_t3 values (5,'田七',22),(6,'赵八',23)

#newdb 库中创建表 t_merge,使用 Merge 引擎,匹配 m 开头的表

node1 :) create table t_merge (id UInt8,name String,age UInt8) engine = Merge(newdb,'^m');

#查询 t_merge 表中的数据

node1 :) select * from t_merge;

Distributed

Distributed ClickHouse 中 分 布 式 引 擎 , 之 前 所 有 的 操 作 虽 然 说 是 在 ClickHouse 集群中进行的,但是实际上是在 node1 节点中单独操作的,与 node2、node3 无关,使用分布式引擎声明的表才可以在其他节点访问与操作。

Distributed 引擎和 Merge 引擎类似,本身不存放数据,功能是在不同的 server 上把多张相同结构的物理表合并为一张逻辑表。

分布式引擎语法:

Distributed(cluster_name, database_name, table_name[, sharding_key])

对以上语法解释:

cluster_name:集群名称,与集群配置中的自定义名称相对应。配置在

/etc/metrika.xml 文件中,如下图:

目录文件有的在:[root@dc-o-ch-08 dev]# cat /etc/clickhouse-server/metrika.xml

database_name:数据库名称。

table_name:表名称。

sharding_key:可选的,用于分片的 key 值,在数据写入的过程中,分布式表

会依据分片 key 的规则,将数据分布到各个节点的本地表。

注意:创建分布式表是读时检查的机制,也就是说对创建分布式表和本地表的顺序并没

有强制要求。

 

例子:

#使用默认的 default 库,在每个节点上创建表 test_table

node1 :) create table test_local (id UInt8,name String) engine= TinyLog

node2 :) create table test_local (id UInt8,name String) engine= TinyLog

node3 :) create table test_local (id UInt8,name String) engine= TinyLog

#node1 上创建分布式表 t_distributed,表引擎使用 Distributed 引擎

node1 :)

create table t_distributed(id UInt8,name String) engine = Distributed(clickhouse_cluster_3shards_1replicas,default,test_local,id);

注意:以上分布式表 t_distributed 只存在与 node1

#分别在 node1node2node3 节点上向表 test_local 中插入 2 条数据

node1 :) insert into test_local values (1,'张三'),(2,'李四');

node2 :) insert into test_local values (3,'王五'),(4,'马六');

node3 :) insert into test_local values (5,'田七'),(6,'赵八');

#查询分布式表 t_distributed 中的数据

node1 :) select * from t_distributed;

#向分布式表 t_distributed 中插入一些数据,然后查询 node1node2node3

点上的 test_local 数据,发现数据已经分布式存储在不同节点上

node1 :) insert into t_distributed values (7,'zs'),(8,'ls'),(9,'ww'),(10,'ml'),(11,'tq'),(12,'zb');

#node1 查询本地表 test_local

以上在 node1 节点上创建的分布式表 t_distributed 虽然数据是分布式存储在每 个 clickhouse 节点上的,但是只能在 node1 上查询 t_distributed 表,其 他 clickhouse 节点查询不到此分布式表。如果想要在每台 clickhouse 节点上都能访问分 布式表我们可以指定集群,创建分布式表:

 #创建分布式表 t_cluster ,引擎使用 Distributed 引擎

create table t_cluster on cluster clickhouse_cluster_3shards_1replicas (id UInt8,name String) engine =Distributed(clickhouse_cluster_3shards_1replicas,default,test_local,id);

中使用了 ON CLUSTER 分布式 DDL(数据库定义语言),这意味着在集群 的每个分片节点上,都会创建一张 Distributed 表,这样便可以从其中任意一端发起对 所有分片的读、写请求。

MergeTree系列表引擎

所有的表引擎中,最为核心的当属 MergeTree 系列表引擎,这些表引擎拥有最为强 大的性能和最广泛的使用场合。对于非 MergeTree 系列的其他引擎而言,主要用于特殊用 途,场景相对有限。而 MergeTree 系列表引擎是官方主推的存储引擎,有主键索引、数据 分区、数据副本、数据采样、删除和修改等功能,支持几乎所有 ClickHouse 核心功能。MergeTree 系 列 表 引 擎 包 含 : MergeTree 、 ReplacingMergeTree 、

SummingMergeTree(汇总求和功能)、AggregatingMergeTree(聚合功能)、

CollapsingMergeTree(折叠删除功能)、VersionedCollapsingMergeTree(版本折叠功能)引擎,在这些的基础上还可以叠加 Replicated DistributedMergeTree 在写入一批数据时,数据总会以数据片段的形式写入磁盘,且数据片段在磁盘上不可修改。为了避免片段过多,ClickHouse 会通过后台线程,定期合并这些数据片段,属于相同分区的数据片段会被合成一个新的片段。这种数据片段往复合并的特点,也 正是合并树名称的由来。

MergeTree 作为家族系列最基础的表引擎,主要有以下特点:

存储的数据按照主键排序:创建稀疏索引加快数据查询速度。

支持数据分区,可以通过 PARTITION BY 语句指定分区字段。

支持数据副本。

支持数据采样

建表语句:

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

关于以上建表语句的解释如下:

ENGINEENGINE = MergeTree()MergeTree 引擎没有参数。

ORDER BY:排序字段。比如 ORDER BY (Col1, Col2),值得注意的是,如果

没有使用 PRIMARY KEY 显式的指定主键 ORDER BY 排序字段自动作为主键。如

果不需要排序,则可以使用 ORDER BY tuple() 语法,这样的话,创建的表也

就不包含主键。这种情况下,ClickHouse 会按照插入的顺序存储数据。必选项。

PARTITION BY : 分 区 字 段 , 例 如 要 按 月 分 区 , 可 以 使 用 表 达 式

toYYYYMM(date_column),这里的 date_column 是一个 Date 类型的列,分

区名的格式会是"YYYYMM"可选。

PRIMARY KEY:指定主键,如果排序字段与主键不一致,可以单独指定主键字段。

否则默认主键是排序字段。大部分情况下不需要再专门指定一个 PRIMARY KEY

子句,注意,MergeTree 中主键并不用于去重,而是用于索引,加快查询速度。

可选。

另外,如果指定了 PRIMARY KEY 与排序字段不一致,要保证 PRIMARY KEY

定的主键是 ORDER BY 指定字段的前缀,比如:

这种强制约束保障了即便在两者定义不同的情况下,主键仍然是排序键的前缀,不

会出现索引与数据顺序混乱的问题。

--允许 ... ... ORDER BY (A,B,C) PRIMARY KEY A --报错 ... ... ORDER BY (A,B,C) PRIMARY KEY B DB::Exception: Primary key must be a prefix of the sorting key

举报

相关推荐

0 条评论