大数据-137 - ClickHouse 集群 表引擎详解2 - MergeTree 存储结构 一级索引 跳数索引

点一下关注吧!!!非常感谢!!持续更新!!!

目前已经更新到了:

  • Hadoop(已更完)
  • HDFS(已更完)
  • MapReduce(已更完)
  • Hive(已更完)
  • Flume(已更完)
  • Sqoop(已更完)
  • Zookeeper(已更完)
  • HBase(已更完)
  • Redis (已更完)
  • Kafka(已更完)
  • Spark(已更完)
  • Flink(已更完)
  • ClickHouse(正在更新···)

章节内容

上节我们完成了如下的内容:

  • 表引擎详解 介绍
  • 日志部分
  • Log部分
  • Memory部分
  • Merge部分

MergeTree

ClickHouse中最强大的表引擎当属 MergeTree(合并树)引擎及该系列(*MergeTree)中的其他引擎。

MergeTree 引擎系列的基本理念如下,当你有巨量数据要插入到表中,你要高效的一批批写入数据片段,并希望这些数据片段在后台按照一定规则合并,相比在插入时不断修改(重写)数据进存储,这种策略会高效很多。

存储结构

创建新表

shell 复制代码
CREATE TABLE mt_table(date Date, id UInt8, name String) 
ENGINE = MergeTree PARTITION BY toYYYYMM(date) ORDER BY id;

CREATE TABLE mt_table3 (
  `date` Date,
  `id` UInt8,
  `name` String
) ENGINE = MergeTree PARTITION BY toYYYYMM(date) ORDER BY id;

执行的结果如下图所示:

插入数据

shell 复制代码
INSERT INTO mt_table VALUES ('2024-07-31', 1, 'wzk');
INSERT INTO mt_table VALUES ('2024-07-30', 2, 'icu');
INSERT INTO mt_table VALUES ('2024-07-29', 3, 'wzkicu');

执行结果如下图所示:

查看目录

shell 复制代码
cd /var/lib/clickhouse/data/default/mt_table
ls

执行结果如下图所示:

我们随便进入一个目录,可以看到:

  • bin 是按列保存数据的文件
  • mrk 保存块偏移量
  • primary.idx 保存主键索引

存储结构

shell 复制代码
.
├── 202407_1_1_0
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   ├── minmax_date.idx
│   ├── partition.dat
│   └── primary.idx
├── 202407_2_2_0
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   ├── minmax_date.idx
│   ├── partition.dat
│   └── primary.idx
├── 202407_3_3_0
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   ├── minmax_date.idx
│   ├── partition.dat
│   └── primary.idx
├── detached
└── format_version.txt

执行结果如下图所示:

  • checknums.txt 二进制校验文件,保存了余下文件的大小size和size的hash值,用于快速校验文件的完整和正确性
  • columns.txt 明文的列信息
  • date.bin 压缩格式(默认LZ4)的数据文件,保存了原始数据,以列名 bin 命名。
  • date.mrk2 使用了自适应大小的索引间隔
  • primary.idx 二进制一级索引文件,在建表的时候通过 order by 或者 primary key 声明稀疏索引。

数据分区

数据是以分区目录的形式组织的,每个分区独立分开存储。这种形式,在数据查询的时候,可以有效的跳过无用的数据文件。

分区规则

分区键的取值,生成分区ID,分区根据ID决定,根据分区键的数据类型不同,分区ID的生成目前有四种规则:

  • 不指定分键
  • 使用整型
  • 使用日期类型 toYYYYMM(date)
  • 使用其他类型
    数据在写入的时候,会按照分区ID落入对应的分区。

分区目录生成

  • BlockNum 是一个全局整型,从1开始,每当新创建一个分区目录,此数字就累加1。
  • MinBlockNum:最小数据块编号
  • MaxBlockNum:最大数据块编号
  • 对于一个新的分区,MinBlockNum和MaxBlockNum的值是相同的

分区目录合并

MergeTree 的分区目录在数据写入过程中被创建,不同的批次写入数据属于同一分区,也会生成不同的目录,在之后某个时刻再合并(写入后10-15分钟),合并后的旧分区目录默认8分钟后删除。

同一个分区的多个目录合并以后得命名规则:

  • MinBlockNum:取同一分区中MinBlockNum值最小的
  • MaxBlockNum:取同一分区中MaxBlockNum最大的
  • Level:取同一分区最大的Level值+1

一级索引

稀疏索引

文件:primary.idx

MergeTree的主键使用PrimaryKey定义,主键定义之后,MergeTree会根据index_granularity间隔(默认8192)为数据生成一级索引并保存至primary.idx中,这种方式就是稀疏索引。

简化形式:通过 ORDER BY 指代 主键

primary.idx 文件的一级索引采用稀疏索引。

  • 稠密索引:每一行索引标记对应一行具体的数据记录
  • 稀疏索引:每一行索引标记对应一段数据记录(默认索引粒度是8192)

稀疏索引占用空间小,所以primary.idx内的索引数据常驻内存,取用速度快。

生成规则

primary.idx文件,由于稀疏索引,所以MergeTree要间隔index_granularity行数据才会生成一个索引记录,其他索引值会根据声明的主键字段获取。

查询过程

索引是如何工作的?对primary.idx文件的查询过程

  • MarkRange: 一小段数据区间,按照 index_granularity的间隔粒度,将一段完整的数据划分成多个小的数据段,小的数据段就是MarkRange
  • MarkRange与索引编号对应

小案例:

  • 200行数据
  • index_granularity大小为5
  • 主键ID为int,取值从0开始

共200行数据/5 = 40个MarkRange

假设索引查询 where Id = 3

  • 第一步:形成区间格式 [3,3]
  • 第二步:进行交集 [3,3] ∩ [0, 199]
    以MarkRange的步长大于8分块,进行剪枝:
  • 第三步:合并, MarkRange(start0, end20)

在ClickHouse中,MergeTree引擎表的索引列在建表使用ORDER BY语法来指定。

而在官方中,用了下面一副图来说明。

这张图示出了以 CounterID、Date两列为索引列的情况,即先以CounterID为主要关键字排序,再以Date为次要关键字排序,最后用两列的组合作为索引键。Marks与MarkNumbers就是索引标记,且Marks之间的间隔就由建表时的索引粒度参数index_granularity来制定,默认是8192。

在 ClickHouse 之父Alexey Milovidov分享的PPT中,有更加详细的图示:

这样,每一列都通过ORDER BY列进行了索引,查询时,先查找到数据所在的parts,再通过mrk2文件确定bin文件中数据的范围即可。

不过,ClickHouse的稀疏索引与Kafka的稀疏索引不同,可以由用户自由组合多列,因此也要格外注意不要加入太多索引列,防止索引数据过于稀疏,增大存储和查找成本。另外,基数太小(即区分度太低)的列不适合做索引列,因为很有可能横跨多个mark值仍然相同,没有索引的意义了。

跳数索引

  • index_granularity 定义了数据的粒度
  • granularity定义了聚合信息汇总的粒度
  • granularity定义了一行跳数索引能够跳过多少个index_granularity区间的数据

可用类型

  • minmax存储指定表达式的极值(如果表达式是tuple,则存储tuple中每个元素的极值),这些信息用于跳过数据块,类似主键
  • set(max_rows)存储指定表达式的唯一值(不超过max_rows个,max_rows=0则表示无限制)。这些信息可以用于检查 WHERE 表达式是否满足某个数据块
  • ngrambf_v1 存储包含数据块中所有N元短语的布隆过滤器。只可用于字符串上,用于优化equals、like和in表达式的性能。
  • tokenbf_v1 跟 ngrambf_v1 类似,不同于ngrams 存储字符串指定长度的所有片段,它只存储被非字母数据字符分割的片段。
相关推荐
beata几秒前
Java基础-9:深入 Java 虚拟机(JVM):从底层源码到核心原理的全面解析
java·后端
py小王子2 分钟前
dy评论数据爬取实战:基于DrissionPage的自动化采集方案
大数据·开发语言·python·毕业设计
SimonKing11 分钟前
分享一款可以管理本地端口的IDEA插件:Port Manager
java·后端·程序员
索荣荣17 分钟前
Maven配置文件(pom.xml)终极指南
java·开发语言
代码栈上的思考30 分钟前
SpringBoot 拦截器
java·spring boot·spring
送秋三十五34 分钟前
一次大文件处理性能优化实录————Java 优化过程
java·开发语言·性能优化
龙山云仓36 分钟前
MES系统超融合架构
大数据·数据库·人工智能·sql·机器学习·架构·全文检索
雨中飘荡的记忆36 分钟前
千万级数据秒级对账!银行日终批处理对账系统从理论到实战
java
jbtianci42 分钟前
Spring Boot管理用户数据
java·spring boot·后端
Sylvia-girl1 小时前
线程池~~
java·开发语言