ClickHouse | 青训营笔记

92 阅读3分钟

ClickHouse | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 13 天,本文是课程「ClickHouse」的学习笔记。

ClickHouse的存储设计

  ClickHouse的架构

  1. 架构图 image.png
  2. 表定义和结构 image.png
  3. 集群架构 image.png

  ClickHouse的存储架构

  1. 数据结构

a.文件组织

image.png 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都有列索引

    索引设计

  1. 主键索引
  2. 数据按照主键顺序一次排序 UserID首先做排序,然后是URL,最后是EventTime
  3. 数据被组织成granule
  • granule是引擎做数据处理的最小数据单位,引擎读数据的时候不是按照一行一行读取的,而是最少读取一个granule
  • 方便构建稀疏索引
  • 方便并行计算
  1. 每个granule都对应primary.idx里面的一行
  2. 默认每8192行记录主键的一行值,primary.idx需要被全部加载到内存里面
  3. 每个主键的一行数据被称为一个mark
  4. 每个列都有这样一个mark文件,mark文件存储所有granule在物理文件里面的地址,每一列都有一个mark文件
  5. mark文件里面的每一行存储两个地址
  • 第一个地址称为block_offset,用于定位一个granule的压缩数据在物理文件中的位置,压缩数据会以一个block为单位解压到内存中。
  • 第二个地址称为granule_offset,用于定位一个granule在解压之后的block中的位置。

索引的缺陷和优化

  1. 缺陷:数据按照key的顺序做排序,因此只有第一个key的过滤效果好,后面的key过滤效果依赖第一个key的基数大小
  2. 二级索引
  • 在URL列上构建二级索引
  1. 构建多个主键索引
  • 再建一个表(数据需要同步两份,查询需要用户判断查哪张表)

  • 建一个物化视图(数据自动同步到隐式表,查询需要用户判断查哪张表)

  • 使用Projection(数据自动同步到隐式表,查询自动路由到最优的表)

  数据合并

  • 一个part内的数据是有序的

  • 不同part之间的数据是无序的

  • 数据合并是将多个part合并成一起的过程

  • part的合并发生在一个分区内

  • 数据的可见性

    数据合并过程中,未被合并的数据对查询可见

    数据合并完成后,新part可见,被合并的part被标记删除