Clickhouse 数据存储与查询

一、数据分区

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

1.1 数据分区的规则

        分区键的取值,生成分区ID,分区根据ID决定。根据分区键的数据类型不同,分区ID的生成目前有四种规则:
(1)不指定分区键
(2)整形
(3)日期类型(主要根据日期进行分区)
(4)其他类型
数据在写入时,会对照分区ID落入对应的分区

1.2分区目录的生成规则

        partitionID_MinBlockNum_MaxBlockNum_Level
BlockNum是一个全局整型,从1开始,每当新创建一个分区目录,此数字就累加1。

MinBlockNum:最小数据块编号。
MaxBlockNum:最大数据块编号。

对于一个新的分区,MinBlockNum和MaxBlockNum的值相同: 2020_03_1_1_0,2020_03_2_2_0
*Level:合并的层级,即某个分区被合并过得次数。不是全局的,而是针对某一个分区。

1.3分区目录的合并过程

MergeTree的分区目录在数据写入过程中被创建。
不同的批次写入数据属于同一分区,也会生成不同的目录,在之后的某个时刻再合并(写入后的10-15分钟),合并后的旧分区目录默认8分钟后删除。
同一个分区的多个目录合并以后的命名规则:
。MinBlockNum: 取同一分区中MinBlockNum值最小的
。MaxBlockNum:取同一分区中MaxBlockNum值最大的
·Level:取同一分区最大的Level值加1

二、索引文件

2.1 稀疏索引

        primary.idx文件的一级索引采用稀疏索引。
        稠密索引: 每一行索引标记对应一行具体的数据记录。

        稀疏索引:每一行索引标记对应一段数据记录(默认索引粒度为8192)。

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

2.2 一级索引

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

        **简化形式:通过order by指代主键**

2.3索引生成规则

三、 索引如何执行查询操作

索引的查询过程
索引是如何工作的?对primaryidx文件的查询过程**MarkRange:一小段数据区间**按照index granularity的间隔粒度,将一段完整的数据划分成多个小的数据段,小的数据段就是MarkRangeMarkRange与索引编号对应
案例
共200行数据
indexgranularity大小为5
主键ID为Int,取值从0开始
根据索引生成规则,primary.idx文件内容为:

 执行过程

        

.bin 原始数据 .mark 索引映射 

 形成一个压缩块

 整体数据查询过程

 

.bin文件形成多个压缩块->.mark文件找到压缩块 ->索引块->解压->再找数据

数据写入过程

 查询过程

四、 二进制文件解释

二进制文件格式详解
在介绍了上述目录下的每个文件的功能和作用后,我们一起来看看上面各项文件中,具体存储了什么信息,以及如何去分析这些二进制文件。

在讲解之前,为了更好的看清文件内容,我们一共在users表中插入5条数据:

insert into test.users values ('zhangsan', 'female', 20, '2023-03-07');
insert into test.users values ('lisi', 'female', 22, '2023-03-06');
insert into test.users values ('lilei', 'female', 25, '2023-03-06');
insert into test.users values ('xiaoming', 'female', 27, '2023-03-09');
insert into test.users values ('yaya', 'female', 30, '2023-03-07');

SELECT * FROM users ORDER BY age DESC

Query id: ee21ad84-be21-47cf-92bd-55059d59fa6e

┌─name─┬─sex────┬─age─┬───birthday─┐
│ yaya │ female │  30 │ 2023-03-07 │
└──────┴────────┴─────┴────────────┘
┌─name─────┬─sex────┬─age─┬───birthday─┐
│ xiaoming │ female │  27 │ 2023-03-09 │
│ lilei    │ female │  25 │ 2023-03-06 │
│ lisi     │ female │  22 │ 2023-03-06 │
│ zhangsan │ female │  20 │ 2023-03-07 │
└──────────┴────────┴─────┴────────────┘

5 rows in set. Elapsed: 0.002 sec. 

primary.idx
MergeTree表会根据排序键生成primary.idx表,由users的建表语句可知,我们设置排序的键为age,同时index_granularity = 2。因此对应primary.idx中生成的记录应该为20、25、30三条记录。接下来,我们进入到primary.idx文件中,看下记录的内容是不是我们逻辑上分析的结果:

hexdump -C primary.idx
00000000  14 19 1e 1e                                       |....|
00000004

hexdump可以用来查看二进制文件的十六进制编码。由于定义的age Int8占用1个字节,因此上面的每一个16进制值应该对应于一个Age值。 对上面的十六进制结果做一下转换:

14(十六进制)->20(十进制)
19(十六进制)->25(十进制)
1e(十六进制)->30(十进制)
可以发现,转换成10进制后跟我们一开始的逻辑分析结果一致。证明了我们的猜想,更进一步核实了Clickhouse中primary.idx稀疏索引的原理。

{column}.mrk2

一个{column}.bin文件有1至多个数据压缩块组成,mark2数据标记文件格式比较固定,primary.idx文件中的每个索引在此文件中都有一个对应的Mark,有三列:
Offset in compressed file,8 Bytes,代表该标记指向的压缩数据块在bin文件中的偏移量
Offset in decompressed block,8 Bytes,代表该标记指向的数据在解压数据块中的偏移量
Rows count,8 Bytes,行数,通常情况下其等于index_granularity

在这里插入图片描述
因此,每一行mrk2文件共占用24 Bytes。所以通过primary.idx中的索引寻找mrk2文件中对应的Mark非常简单,如果要寻找第n(从0开始)个index,则对应的Mark在mrk2文件中的偏移为n*24,从这个偏移处开始读取24 Bytes即可得到相应的Mark。

age.mrk2

$ hexdump -C age.mrk2
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000010  02 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  02 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000040  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  05 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060

$ od -An -l age.mrk2
                    0                    0
                    2                    0
                    2                    2
                    0                    4
                    1                    0
                    5                    0

分析下上面的文件内容。由于24 Bytes表示一行数据,因此从上面以每24位做切分,可以得到如下所示的表:

由于最后一个mark对应的数据只有一条,所以最后一个行数为1。从表中可以看出,解压文件中的偏移量对应的原表中的age值这与我们在primary.idx中分析出来的3个索引值一一对应。
index_granularity =2,且定义的age Int8占用1个字节,所以每两行对应一个索引,mrk2中也是没两行生成一条对应mark。
由于本次存储的数据 < 默认的压缩大小块64KB,因此所有的数据都在一个压缩块内,压缩文件中的偏移量都是0。
由于最后一个mark对应的数据只有一条,所以最后一个行数为1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值