ClickHouse深入浅出之(三) 完结篇 (引擎)

x33g5p2x  于2021-12-25 转载在 其他  
字(7.0k)|赞(0)|评价(0)|浏览(430)

一、表引擎

表引擎在 ClickHouse 中的作用十分关键,直接决定了数据如何存储和读取、是否支持并发读写、是否 支持 index、支持的 query 种类、是否支持主备复制等。如果你需要创建分区表,简单的 TinyLog 没有, Memory 也没有!

1.1、表引擎概述

ClickHouse 提供了大约 28 种表引擎,各有各的用途,比如有 Log 系列用来做小表数据分析, MergeTree 系列用来做大数据量分析,而 Integration 系列则多用于外表数据集成。再考虑复制表 Replicated 系列,分布式表 Distributed 等,纷繁复杂,新用户上手选择时常常感到迷惑。

ClickHouse表引擎一共分为四个系列,分别是Log、MergeTree、Integration、Special。其中包含了两种特殊的表引擎Replicated、Distributed,功能上与其他表引擎正交,根据场景组合使用。最强大的表 引擎当属 MergeTree (合并树)引擎及该系列(*MergeTree)中的其他引擎。对于大多数正式的任务,推荐使用MergeTree 族中的引擎。

Log、Special、Integration 主要用于特殊用途,场景相对有限。MergeTree 系列才是官方主推的存储 引擎,支持几乎所有 ClickHouse 核心功能。

1.2. 表引擎概览

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

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

ClickHouse 的表引擎有很多,下面介绍其中几种,对其他引擎有兴趣的可以去查阅官方文档:https://clickhouse.yandex/docs/zh/operations/table_engines/

二、表引擎类型

2.1. Log系列

Log系列表引擎功能相对简单,主要用于快速写入小表(1百万行左右的表),然后全部读出的场景。当你需要快速写入许多小表(最多约100万行)并在以后整体读取它们时,该类型的引擎是最有效的。

几种Log表引擎的共性是:

1、数据被顺序append写到磁盘上; 
2、不支持delete、update; 
3、不支持index; 
4、不支持原子性写; 
5、insert会阻塞select操作。

主要特点:

1、数据存储在磁盘上。
2、写入时将数据追加在文件末尾。
3、不支持突变操作。
4、不支持索引。意味着 SELECT 在范围查询时效率不高。 
5、非原子地写入数据。如果某些事情破坏了写操作,例如服务器的异常关闭,你将会得到一张包含了损坏数 据的表。

该类型的引擎有:

1、TinyLog 
2、StripeLog 
3、Log

它们彼此之间的区别是: 是否支持并发读写,性能问题,列存储问题

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

2.1.1. TinyLog

最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入时,数据将附加到
文件末尾。

该引擎没有并发控制:

1、如果同时从表中读取和写入数据,则读取操作将抛出异常; 
2、如果同时写入多个查询中的表,则数据将被破坏。

这种表引擎的典型用法是 write-once:首先只写入一次数据,然后根据需要多次读取。查询在单个流中执行。换句话说,此引擎适用于相对较小的表(建议最多1,000,000行)。如果您有许多小表,则使用此表引擎是适合的,因为它比 Log 引擎更简单(需要打开的文件更少)。当您拥有大量小表时,可能会导致性能低下,但在可能已经在其它 DBMS 时使用过,则您可能会发现切换使用 TinyLog 类型的表更容易。不支持索引。

2.1.2. Log

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

2.1.3. StripeLog

StripeLog 引擎将所有列存储在一个文件中。对每一次 Insert 请求,ClickHouse 将数据块追加在表文件的末尾,逐列写入。在你需要写入许多小数据量(小于一百万行)的表的场景下使用这个引擎。

StripeLog 引擎不支持 ALTER UPDATE 和 ALTER DELETE 操作。

2.2. Integration系列

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

Kafka:将Kafka Topic中的数据直接导入到ClickHouse; 
MySQL:将MySQL作为存储引擎,直接在ClickHouse中对MySQL表进行select等操作; 
JDBC/ODBC:通过指定jdbc、odbc连接串读取数据源; 
HDFS:直接读取HDFS上的特定格式的数据文件;

2.3. Special系列

Special系列的表引擎,大多是为了特定场景而定制的。这里也挑选几个简单介绍,不做详述。

Memory:将数据存储在内存中,重启后会导致数据丢失。查询性能极好,适合于对于数据持久性没有要求的1 亿一下的小表。在ClickHouse中,通常用来做临时表。
Buffer:为目标表设置一个内存buffer,当buffer达到了一定条件之后会flush到磁盘。 
File:直接将本地文件作为数据存储;
Null:写入数据被丢弃、读取数据为空;

2.3.1. Memory

内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现(超过10G/s)。

一般用到它的地方不多,除了用来测试,就是在需要非常高的性能,同时数据量又不太大(上限大概 1 亿行)的场景。

Memory 引擎以未压缩的形式将数据存储在 RAM 中。数据完全以读取时获得的形式存储。换句话说, 从这张表中读取是很轻松的。并发数据访问是同步的。锁范围小:读写操作不会相互阻塞。不支持索 引。阅读是并行化的。在简单查询上达到最大生产率(超过10 GB /秒),因为没有磁盘读取,不需要 解压缩或反序列化数据。(值得注意的是,在许多情况下,与 MergeTree 引擎的性能几乎一样高)。 重新启动服务器时,表中的数据消失,表将变为空。通常,使用此表引擎是不合理的。但是,它可用于 测试,以及在相对较少的行(最多约100,000,000)上需要最高性能的查询。

2.3.2. Merge

Merge 引擎 (不要跟 MergeTree 引擎混淆) 本身不存储数据,但可用于同时从任意多个其他的表中读取数据。 读是自动并行的,不支持写入。读取时,那些被真正读取到数据的表的索引(如果有的话)会 被使用。

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

ENGINE=Merge(db, 'regex')

2.3.3. Distributed

分布式引擎,本身不存储数据,但可以在多个服务器上进行分布式查询。 读是自动并行的。读取时,远程服务器表的索引(如果有的话)会被使用。

Distributed(cluster_name, database, table [, sharding_key])

参数解析:

关于整合:

1、merge引擎: 在同一个server上,多个相同结构的物理表,可以被整合成一张大的逻辑表,这张逻辑表的数据,就是包含了这些物理表中的所有数据。

2、distributed: 在不同的server上,多个相同结构的物理表,可以被整合成一张大的逻辑表,这张逻 辑表的数据,就是包含了这些物理表中的所有数据。

2.3.4. MergeTree系列

ClickHouse拥有非常庞大的表引擎体 系,其共拥有合并树、外部存储、内存、文件、接口和其他6大类20多种表引擎。而在这众多的表引擎 中,又属合并树(MergeTree)表引擎及其家族系列(*MergeTree)最为强大,在生产环境的绝大部 分场景中,都会使用此系列的表引擎。因为只有合并树系列的表引擎才支持主键索引、数据分区、数据 副本和数据采样这些特性,同时也只有此系列的表引擎支持ALTER相关操作。

合并树家族自身也拥有多种表引擎的变种。其中MergeTree作为家族中最基础的表引擎,提供了主键 索引、数据分区、数据副本和数据采样等基本能力,而家族中其他的表引擎则在MergeTree的基础之 上各有所长。例如ReplacingMergeTree表引擎具有删除重复数据的特性,而SummingMergeTree表引 擎则会按照排序键自动聚合数据。如果给合并树系列的表引擎加上Replicated前缀,又会得到一组支持 数据副本的表引擎,例如ReplicatedMergeTree、ReplicatedReplacingMergeTree、 ReplicatedSummingMergeTree等。

2.3.4.1、MergeTree创建方式

CREATE TABLE [IF NOT EXISTS] [db_name.]table_name( name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], name2[type] [DEFAULT|MATERIALIZED|ALIAS expr], 省略...
)ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value,省略...]

参数解析:

1、PARTITION BY [选填]:分区键,用于指定表数据以何种标准进行分区.
2、ORDER BY [必填]:排序键,用于指定在一个数据片段内,数据以何种标准排序。
3、PRIMARY KEY [选填]:主键,顾名思义,声明后会依照主键字段生成一级索引,用于加速表查询。
4、SAMPLE BY [选填]:抽样表达式,用于声明数据以何种标准进行采样。
5、SETTINGS: index_granularity [选填]:index_granularity对于MergeTree而言是一项非常重要的 参数,它表示索引的粒度,默认值为 8192。也就是说,MergeTree的索引在默认情况下,每间隔8192 行数据才生成一条索引。
6、SETTINGS: index_granularity_bytes [选填]:在19.11版本之前,ClickHouse只支持固定大小的 索引间隔,由index_granularity控制,默认为8192。在新版本中,它增加了自适应间隔大小的特性, 即根据每一批次写入数据的体量大小,动态划分间隔大小。而数据的体量大小,正是由 index_granularity_bytes参数控制的,默认为10M(10×1024×1024),设置为0表示不启动自适应功能。 每条记录 1kb
7、SETTINGS: enable_mixed_granularity_parts [选填]:设置是否开启自适应索引间隔的功能,默 认开启。
8、SETTINGS: merge_with_ttl_timeout [选填]:从19.6 版本开始,MergeTree 提供了数据 TTL 的功 能,可以选择性的让某个列,或者某个表设置自动过期时间。
9、SETTINGS: storage_policy [选填]:从19.15 版本开始,MergeTree 提供了多路径的存储策略,为 应对大数据量的存储提供了方案。

2.3.4.2、MergeTree结构存储

一张数据表的完整物理结构分为3个层级,依次是数据表目录、分区目录及各分区下具体的数据文件。 接下来就逐一介绍它们的作用。

(1)partition:分区目录,余下各类数据文件(primary.idx、[Column].mrk、[Column]. bin等)都 是以分区目录的形式被组织存放的,属于相同分区的数据,最终会被合并到同一个分区目录,而不同分 区的数据,永远不会被合并在一起。

(2)checksums.txt:校验文件,使用二进制格式存储。它保存了余下各类文件(primary. idx、count.txt等)的size大小及size的哈希值,用于快速校验文件的完整性和正确性。

(3)columns.txt:列信息文件,使用明文格式存储。用于保存此数据分区下的列字段信息。

(4)count.txt:计数文件,使用明文格式存储。用于记录当前数据分区目录下数据的总行数。

(5)primary.idx:一级索引文件,使用二进制格式存储。用于存放稀疏索引,一张MergeTree表只 能声明一次一级索引(通过ORDER BY或者PRIMARY KEY)。借助稀疏索引,在数据查询的时能够排除 主键条件范围之外的数据文件,从而有效减少数据扫描范围,加速查询速度。

(6)[Column].bin:数据文件,使用压缩格式存储,默认为LZ4压缩格式,用于存储某一列的数据。 由于MergeTree采用列式存储,所以每一个列字段都拥有独立的 .bin 数据文件,并以列字段名称命名 (例如CounterID.bin、EventDate.bin等)。

(7)[Column].mrk:列字段标记文件,使用二进制格式存储。标记文件中保存了.bin文件中数据的 偏移量信息。标记文件与稀疏索引对齐,又与 .bin 文件一一对应,所以MergeTree通过标记文件建立 了primary.idx稀疏索引与 .bin 数据文件之间的映射关系。即首先通过稀疏索引(primary.idx)找到对 应数据的偏移量信息(.mrk),再通过偏移量直接从.bin文件中读取数据。由于.mrk标记文件与. bin文件一一对应,所以MergeTree中的每个列字段都会拥有与其对应的.mrk标记文件(例如 CounterID.mrk、EventDate.mrk等)。

(8)[Column].mrk2:如果使用了自适应大小的索引间隔,则标记文件会以 .mrk2 命名。它的工作原 理和作用与 .mrk 标记文件相同。

(9)partition.dat 与 minmax_[Column].idx:如果使用了分区键,例如 PARTITION BY toYYYYMM(date) ,则会额外生成 partition.dat 与 minmax 索引文件 minmax_date.idx,它们均使用 二进制格式存储。partition.dat 用于保存当前分区下分区表达式最终生成的值;而 minmax_date.idx 用于记录当前分区下分区字段对应原始数据的最小和最大值。例如date字段对应的原始数据为2019-05- 01、2019-05-05,分区表达式为PARTITION BY toYYYYMM(date)。partition.dat中保存的值将会是 2019-05,而 minmax_date.idx 中保存的值将会是 2019-05-012019-05-05。在这些分区索引的作用 下,进行数据查询时能够快速跳过不必要的数据分区目录,从而减少最终需要扫描的数据范围。

(10)skp_idx_[Column].idx与skp_idx_[Column].mrk:如果在建表语句中声明了二级索引,则会 额外生成相应的二级索引与标记文件,它们同样也使用二进制存储。二级索引在ClickHouse中又称跳数 索引,目前拥有minmax、set、ngrambf_v1和tokenbf_v1四种类型。这些索引的最终目标与一级稀疏 索引相同,都是为了进一步减少所需扫描的数据范围,以加速整个查询过程。

2.3.4.3、MergeTree数据分区

(1)不指定分区键:如果不使用分区键,即不使用PARTITION BY声明任何分区表达式,则分区ID默认 取名为all,所有的数据都会被写入这个all分区。

(2)使用整型:如果分区键取值属于整型(兼容UInt64,包括有符号整型和无符号整型),且无法转换为 日期类型YYYYMMDD格式,则直接按照该整型的字符形式输出,作为分区ID的取值。

(3)使用日期类型:如果分区键取值属于日期类型,或者是能够转换为YYYYMMDD格式的整型,则使 用按照YYYYMMDD进行格式化后的字符形式输出,并作为分区ID的取值。

(4)使用其他类型:如果分区键取值既不属于整型,也不属于日期类型,例如String、Float等,则通 过128位Hash算法取其Hash值作为分区ID的取值。数据在写入时,会对照分区ID落入相应的数据分区。

相关文章