ClickHouse笔记(三) | 青训营笔记

100 阅读3分钟

这是我参与「第五届青训营 」笔记创作活动的第15天

相关笔记ClickHouse笔记(一) | 青训营笔记

相关笔记ClickHouse笔记(二) | 青训营笔记

一、本文内容:

  1. 数据库基本概念

  2. 列式存储

  3. ClickHouse存储设计

  4. ClickHouse典型应用场景

二、详细介绍

ClickHouse的存储设计

ClickHouse的架构

  1. 架构图

  1. 表定义和结构

  1. 集群架构

ClickHouse的存储架构

  1. 数据结构

a.文件组织

b.文件内容

c. part和partition

  • part是物理文件夹的名字
  • partition是逻辑结构

d. part和column

  • 每个column都是一个文件
  • 所有的column文件都在自己的part文件夹下

e. column和index

  • 一个part有一个主键索引
  • 每个column都有列索引

索引设计

  1. 主键索引
CREATE TABLE hits_UserID_URL
(
    `UserID` UInt32,
    `URL` String,
    `EventTime` DateTime
)
ENGINE = MergeTree
PRIMARY KEY (UserID, URL)
ORDER BY (UserID, URL, EventTime)
SETTINGS index_granularity = 8192, index_granularity_bytes = 0;
  1. 数据按照主键顺序依次排序 UserID首先做排序,然后是URL,最后是EventTime

  1. 数据被组织成granule
  • granule是引擎做数据处理的最小数据单位,引擎读数据的时候不是按照一行一行读取的,而是最少读取一个granule
  • 方便构建稀疏索引
  • 方便并行计算

  1. 每个granule都对应primary.idx里面的一行

  1. 默认每8192行记录主键的一行值,primary.idx需要被全部加载到内存里面

  1. 每个主键的一行数据被称为一个mark

  1. 每个列都有这样一个mark文件,mark文件存储所有granule在物理文件里面的地址,每一列都有一个mark文件

  1. mark文件里面的每一行存储两个地址
  • 第一个地址称为block_offset,用于定位一个granule的压缩数据在物理文件中的位置,压缩数据会以一个block为单位解压到内存中。
  • 第二个地址称为granule_offset,用于定位一个granule在解压之后的block中的位置。

索引的缺陷和优化

  1. 缺陷:数据按照key的顺序做排序,因此只有第一个key的过滤效果好,后面的key过滤效果依赖第一个key的基数大小

  1. 二级索引
  • 在URL列上构建二级索引

  1. 构建多个主键索引
  • 再建一个表(数据需要同步两份,查询需要用户判断查哪张表)

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

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

数据合并

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

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

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

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

  • 数据的可见性
  • 数据合并过程中,未被合并的数据对查询可见
  • 数据合并完成后,新part可见,被合并的part被标记删除

数据查询

  1. 对于查询
SELECT
    URL,
    count(URL) AS Count
FROM hits_UserID_URL
WHERE UserID = 749927693GROUP BY URL
ORDER BY Count DESC
LIMIT 10
  1. 通过主键找到需要读的mark
  2. 切分marks,然后并发的调度reader

  1. Reader 通过mark block_offset得到需要读的数据文件的偏移量
  2. Reader 通过mark granule_offset得到解压之后数据的偏移量

  1. 构建列式filter做数据过滤

本文如有不足之处,欢迎大家指出