ClickHouse | 青训营笔记
这是我参与「第五届青训营」伴学笔记创作活动的第 13 天,本文是课程「ClickHouse」的学习笔记。
ClickHouse的存储设计
ClickHouse的架构
- 架构图
- 表定义和结构
- 集群架构
ClickHouse的存储架构
- 数据结构
a.文件组织
b.文件内容
对于表
CREATE TABLE test.test_insert_local
(
`p_date` Date,
`id` Int32
)
ENGINE = MergeTree
PARTITION BY p_date
ORDER BY id
SETTINGS index_granularity = 8192
它的文件组织
├── 20220101_1_1_0
│ ├── checksums.txt
│ ├── columns.txt
│ ├── count.txt
│ ├── data.bin
│ ├── data.mrk3
│ ├── default_compression_codec.txt
│ ├── minmax_p_date.idx
│ ├── partition.dat
│ ├── primary.idx
│ └── versions.txt
├── 20220102_2_2_0
│ ├── checksums.txt
│ ├── columns.txt
│ ├── count.txt
│ ├── data.bin
│ ├── data.mrk3
│ ├── default_compression_codec.txt
│ ├── minmax_p_date.idx
│ ├── partition.dat
│ ├── primary.idx
│ └── versions.txt
├── detached
└── format_version.txt
c. part和partition
-
part是物理文件夹的名字
-
partition是逻辑结构 d. part和column
-
每个column都是一个文件
-
所有的column文件都在自己的part文件夹下
e. column和index
- 一个part有一个主键索引
- 每个column都有列索引
索引设计
- 主键索引
- 数据按照主键顺序一次排序 UserID首先做排序,然后是URL,最后是EventTime
- 数据被组织成granule
- granule是引擎做数据处理的最小数据单位,引擎读数据的时候不是按照一行一行读取的,而是最少读取一个granule
- 方便构建稀疏索引
- 方便并行计算
- 每个granule都对应primary.idx里面的一行
- 默认每8192行记录主键的一行值,primary.idx需要被全部加载到内存里面
- 每个主键的一行数据被称为一个mark
- 每个列都有这样一个mark文件,mark文件存储所有granule在物理文件里面的地址,每一列都有一个mark文件
- mark文件里面的每一行存储两个地址
- 第一个地址称为block_offset,用于定位一个granule的压缩数据在物理文件中的位置,压缩数据会以一个block为单位解压到内存中。
- 第二个地址称为granule_offset,用于定位一个granule在解压之后的block中的位置。
索引的缺陷和优化
- 缺陷:数据按照key的顺序做排序,因此只有第一个key的过滤效果好,后面的key过滤效果依赖第一个key的基数大小
- 二级索引
- 在URL列上构建二级索引
- 构建多个主键索引
-
再建一个表(数据需要同步两份,查询需要用户判断查哪张表)
-
建一个物化视图(数据自动同步到隐式表,查询需要用户判断查哪张表)
-
使用Projection(数据自动同步到隐式表,查询自动路由到最优的表)
数据合并
-
一个part内的数据是有序的
-
不同part之间的数据是无序的
-
数据合并是将多个part合并成一起的过程
-
part的合并发生在一个分区内
-
数据的可见性
数据合并过程中,未被合并的数据对查询可见
数据合并完成后,新part可见,被合并的part被标记删除