这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天。
📜📜📜今天学习的是ClickHouse——第二部分。昨天我们了解了clickhouse使用了列式存储。接下来我们继续学习clickhouse的存储设计
三、clickhouse存储设计
3.1 表定义和结构
- 分布式表:不存储数据,用于将查询路由到集群的各个节点
- cluster:逻辑集群,由多个节点组成
- shard_key:指导数据写入分布式表时的分布方式
- 本地表:实际存储数据的表
create table t_name [on cluster cluster]——这里是选择集群,将表格建在该集群上。
{
name1 [type] [default|]materialized|alias expr1] [ttl expr1],
……
index index_name1 expr1 type type1(...) granularity value1,——这里是建立索引
……
}engine = MergeTree()——引擎选择
order by expr——按照什么排序
[partition by expr]——数据如何在单节点分布
[primary key expr]
[sample by expr]
[ttl expr [delete |to disk 'xxx'|to volume 'xxx'], ...]
……
3.2 集群架构
3.3 引擎架构
3.4 存储架构
表名下面还有很多目录,每一个目录名字叫part,part下面还有很多目录或者是文件,我们在表里定义的每一个字段,都是单独存储成一个数据文件。例如:
下面是文件组织:
不同的partition是不会写到同一个文件夹里面的
part和partition:
- part是物理文件夹的名字
- partition是逻辑结构
3.5 索引设计
hash index:其实就是通过一个hashfunction映射到buckets表里面去(buckets表里面存的是每个key所在文件的地址)
只能等值比较,不适合聚合的大范围的查询。
- B tree
- B+ tree
注意:数据都存在叶子节点,叶子节点是双向列表
log-structured merge-tree (LSM tree)是一种为大吞吐写入场景而设计的数据结构
- 着重优化顺序写入
- 主要数据结构
- SSTables 2.Memtable
3.6 索引实现
主键查询
数据按照主键顺序依次做排序
- 首先按照UserlD做排序
- 再按照URL排序
- 最后是EventTime
数据被划分为granules(力度)
- granules是最小的数据读取单元 2.不同的granulas可以并行读取 (每个granules都对应primary.idx里面的一行)
默认每8192行记录主键的一行值,primary.idx需要被全部加载到内存里面
每个列都有这样一个mark文件
- mark文件保存的是每个granules的物理地址
- 每—列都有一个自己的mark文件
mark文件里面的每一行保存两个地址:
- block_offset:用于定位一个granule的压缩数据在物理文件中的位置,压缩数据会以一个block为单位解压到内存中。
- granule_offset,用于定位一个granule在解压之后的block中的位置。
缺陷:数据按照key的顺序做排序,因此只有第一个key的过滤效果好,后面的key过滤效果依赖第一个key的基数大小
3.7 查询优化
secondary index(二级索引):在URL列上构建二级索引。
构建多个主键索引 – 再建一个表,使用需要优化的字段做主键第一位
- 建一个物化视图
- 使用Projection
3.8 数据合并
将多个part合并成一个。 数据的可见性:
- 数据合并过程中,未被合并的数据对查询可见
- 数据合并完成后,新part可见,被合并的part被标记删除
四、clickhouse典型应用场景
大宽表存储和查询: 1.大宽表查询
- 可以建非常多的列
- 可以增加,删除,清空每一列的数据
- 查询的时候引擎可以快速选择需要的列
- 可以将列涉及到的过滤条件下推到存储层从而加速查询
大宽表存储和查询:2动态表结构
- map中的每个key都是一列
- map中的每一列都可以单独的查询
- 使用方式同普通列,可以做任何计算
离线数据分析 1.数据导入
- 数据可以通过spark生成clickhouse格式的文件
- 导入到hdfs上由 hive2ch导入工具完成数据导入
- 数据直接导入到各个物理节点
2.数据按列导入
- 保证查询可以及时访问已有数据
- 可以按需加载需要的列
实时数据分析 使用memory table减少parts数量
- 数据先缓存在内存中
- 到达一定阈值再写到磁盘