Clickhouse MergeTree 表的数据存储结构

前言

表引擎是 Clickhouse 中的一大特色,不同的表引擎决定了表不同的特性,如:

  • 数据存储方式
  • 支持哪些查询
  • 并发数据访问
  • 索引的使用
  • 是否支持多线程执行
  • 数据副本参数

截至目前为止,clickhosue 光表引擎就有 34 种,而在这么多表引擎中,MergeTree 最常用,功能也最强大,支持主键索引、数据分区、数据副本等特性,所以大多数场景都是用 MergeTree 引擎。所以这里只介绍 MergeTree 引擎

MergeTree 表结构

在 clickhouse 中,一张表对应一个目录,以下面的表为例

create table test (
  id String,
  name String,
  city Nullable(String),
  sex UInt8,
  create_time DateTime
engine = MergeTree
partition by toYYYYMM(create_time)
order by id;

test 表的目录结构如下

table-name
    |
    |---- partition_[min_block_num]_[max_block_num]_[level1]
    |          |
    |          |---- columns.txt
    |          |---- primary.idx
    |          |---- count.txt
    |          |---- data.bin
    |          |---- data.mrk
    |          |---- data.mrk3
    |          |---- checksums.txt
    |          |---- partition.dat
    |          |---- minmax_[column].idx
    |          |---- skp_idx_[indexname].idx
    |          |---- skp_idx_[indexname].mrk3
    |
    |---- partition_[min_block_num]_[max_block_num]_[level2]
    |
    |---- ....
    |
    |---- partition_[min_block_num]_[max_block_num]_[leveln]

下面分别介绍每个文件/文件夹的作用,为方便理解,我们用下面的表举例

create table test (
  id String,
  name String,
  city Nullable(String),
  sex UInt8,
  create_time DateTime
engine = MergeTree
partition by toYYYYMM(create_time)
order by id;

insert into test(1,'huangxy','guangzhou',1,'2024-01-01 00:00:00');

[partition]_[min_block_num][max_block_num]_[level] 目录

MergeTree 数据表是以分区目录进行组织的,每个分区会生成一个单独的目录,min_block_num、max_block_num 为分区最小数据块、分区最大数据块的编号。level 为合并等级,初始值为 0,当发生一次分区合并的时候,level 会自增 1

举个例子,如果我们先 test 表中插入以下数据

insert into test(1,'huangxy','guangzhou',1,'2024-01-01 00:00:00');

分区目录的名称为:202404_1_1_0,再次插入数据

insert into test(2,'huangxy2','guangzhou',1,'2024-01-02 00:00:00');

会生成额外的分区目录,名称为:202404-2-2-0,因为两批数据其实都是属于同个分区(202404分区),当发生分区合并的时候,两个分区会被合并为 202404-1-2-1 分区

可以通过执行optimize table [tablename]语句手动触发分区合并,看看合并后的效果

columns.txt

使用明文方式保存列信息,比如上的 test 表,columns.txt 文件的内容如下

columns format version: 1
2 columns:
`id` String
`city` Nullable(String)

primary.idx

一级索引(也叫稀疏索引)文件,使用二进制存储,借助一级索引,可以有效的减少数据扫描的范围,加速查询速度

count.txt

计数文件,存储当前分区下的数据总数,在查询数据总数的时候,clickhouse 读的就是这个文件的值,不用扫描整张表的数据的行数

data.bin

看名字就知道,data.bin 是数据文件,用于存储表的数据,采用压缩格式存储(默认LZ4压缩格式)

data.mrk & data.mrk3

标记文件,标记 data.bin 文件中数据的偏移量,并且 data.mrk 与 primary.idx 对齐。可以简单理解 data.mrk 是 primary.idx 与 data.bin 的中间文件,在数据查找时,稀疏索引 primary.idx 文件通过 data.mrk 文件找到对应数据的偏移量,即数据在 data.bin 中的位置

如果使用自适应大小的索引间隔,则标记文件会以 data.mrk3 结尾,工作原理跟 data.mrk 一样

partition.dat & minmax_[column].idx

如果建表的时候使用了分区健,如partiotin by toYYYYMM(create_time),则会生成 partition.dat 和 minmax_create_time.idx 文件,两者都为二进制格式。

partition.dat 用于保存分区表达式最终生成的值,比如我们向表中插入 3 条数据,create_time 分别是 2024-04-08、2024-04-1-、2024-04-29,这几条记录都会进入到 2024-04 分区,因为 toYYYYMM() 后的结果就是 2024-04,所以 partition.dat 存储的就是 2024-04,也就是 toYYYYMM() 分区表达式最终生成的值

minmax_create_time.idx 用于保存分区字段对应的原始数据的最小值跟最大值,因为我们的分区健是,还是 2024-04-08、2024-04-1-、2024-04-29 三条数据,最小值是 2024-04-08,最大值是 2024-04-29,所以 minmax_create_time.idx 文件存储的就是 2024-04-08 和 2024-04-29

skp_idx_[indexname].idx & skp_idx_[indexname].mrk3

如果使用了二级索引(又叫跳数索引),则会额外生成 skip_idx_[indexname].idx 和 skp_idx_[indexname].mrk3 文件,作用和 primary.idx 和 data.mrk3 一样


原文始发于微信公众号(huangxy):Clickhouse MergeTree 表的数据存储结构

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/286157.html

(0)
码上实战的头像码上实战

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!