这是我参与「第五届青训营 」伴学笔记创作活动的第17天
前言
本文主要讲解了ClickHouse的存储设计和应用场景,并复习了一些关于数据库和列式存储的基本概念,让我们更加明白ClickHouse的原理和应用。本文主要是对知识的复习和巩固,具体实践需要大家认真应用一下。
1.数据库
此处进行简单的复习,详细的知识之前文章已经进行讲述。
1.数据库概念
数据库是结构化信息或数据的有序集合,通常由数据库管理系统(DBMS)来控制。 在现实中,数据、DBMS及关联应用一起被称为数据库系统,通常简称为数据库。
简单来说,数据解析整理成有序集合,通过查询语言获得想要的信息。
2.数据库类型
- 关系数据库:数据以表形式储存
- 非关系数据库:没有固定的表结构
- 单机数据库:一台计算机完成数据的存储和查询
- 分布式数据库:不同站点的多个文件存储
- OLTP数据库:高速分析数据库,专为多个用户执行大量事务
- OLAP数据库:同时分析多个数据维度,便于理解数据中的复杂关系
这里主要讲解OLAP数据库
- 特点:
- 大量数据读写,PB级存储
- 多维分析,复杂聚合函数
- 窗口函数,自定义UDF
- 离线/实时分析
3.SQL
编程语言,查询、操作和定义数据,进行数据访问控制
-
SELECT:查询字段
-
FROM:指定选择的数据表
-
WHERE:限制返回行
-
GROUP BY;有相同值的行合并
-
优点:
- 标准化,ISO和ANSI
- 高度非过程化,用户只用提出做什么,大大减轻了用户负担,有利于提高数据独立性
- 以同一种语法结构提供两种使用方式(用户和程序员)
- 语言简洁,易学易用
-
架构如下:
- Parser:词法分析,语法分析,生成AST树
- Analyzer:变量绑定,类型推导,语义检查等,为生成计划做准备
- Optimizer:为查询生成性能最优的执行计划,进行代价评估
- Executor:将执行计划翻译成可执行的物理计划并驱动其执行
4.存储引擎
- 管理内存数据结构(索引、内存数据、缓存)
- 管理磁盘数据(文件格式、增删查改)
- 读写算子(写入逻辑、读取逻辑)
2.列式存储
1.优点
- 数据压缩
- 数据选择
- 延迟物化
- 向量化
2.数据压缩
- 读的数据少,性能更好
- 相同类型压缩效率高
- 排序后压缩效率高
- 针对不同类型用不同压缩算法
- LZ4:重复项越多或越长,压缩率越高,如:abc_(5,4)
- Run-length:压缩重复数据,压缩后的数据可直接计算,如:12w
- Delta encoding:将数据存储为连续数据间的差异,特定算子可直接在压缩数据上计算,如:190(base),30,-20
3.数据选择
- 可选择特定列计算而不是读所有列
- 对聚合计算友好
4.延迟物化
- 物化:将列数据转化为可被计算或输出的行数据或内存数据结构的过程,物化后的数据可用来做数据过滤、聚合计算
- 延迟物化:尽可能推迟物化操作发生
- 优点
- 缓存友好
- CPU/内存宽带友好
- 可利用到执行计划和算子的优化
- 保留直接在压缩列做计算的机会
5.向量化
用SIMD指令完成的代码设计和执行逻辑
- SIMD:用一套指令执行多条数据,指令集SSE和AVX
- 数据格式:处理多个数据,需要连续内存;明确数据类型
- 执行模型:数据要按批读取;明确数据类型
PS;行存VS列存
3.ClickHouse存储
1.表
- 分布式表:不存储数据,将查询路由到集群的各个节点
- cluster:逻辑集群,由多个节点组成
- Shard_key:指导数据写入分布式表时的分布方式
- 本地表:实际存储数据表
2.集群架构
- 每个shard都有备份Replica
3.引擎架构
- 和SQL的架构很像
4.存储架构
文件组织
part&partition
- part是物理文件名
- partition是逻辑结构
part&column
- 每个column都是一个文件
- 所有column文件都在自己的part文件下
column&index
- 一个part有一个主键索引
- 每个column都有列索引
5.索引设计
-
Hash Index
- 将输入的key通过一个HashFunction映射到一组bucket上
- 每个bucket都包含一个指向一条记录的地址
- 哈希索引在查找时只适用等值比较
-
B-Tree
- 数据写入有序,支持增删查改
- 每个节点有多个孩子节点
- 每个节点都按照升序排列key值
- 每个key有两个指向左右孩子节点的引用(左孩子节点保存的key都小于当前key;右孩子节点的保存的key都大于当前key)
-
B+Tree
- 所有的数据都存储在叶子节点,非叶子节点只保存key值
- 叶子节点维护到相邻叶子节点的引用
- 可以通过key值做二分查找,也可以通过叶子节点做顺序访问
LSM-Tree
-
背景
- 对于大数据量,B(B+)-Tree深度太高
- 索引数据量太大,多个列需要平衡查询和存储
- OLAP场景写入量非常大,需要优化写入
-
定义:一种为大吞吐写入场景而设计的数据结构,优化顺序写入
-
主要数据结构:SSTables、Memtable
-
SSTables:Key按顺序存储到文件中,称为segment;包含多个segment;每个segment写入磁盘后都是不可更改的,新加的数据只能生成新的segment
-
Memtable:存储数据到达一定的阈值时,会按顺序写入磁盘
-
数据查询:从最新segment遍历每个key;也可以为每个segment建一个索引
-
合并:多个segments合并为一个segment的过程;后台线程完成;新的segment完成后旧的segment被删除
6.索引实现
步骤
- 主键索引:先按照UserID排序,再按照URD排序,最后是EventTime
- 数据划分为granules;其为最小的数据读取单元;不同的granules可并行读取
- 每个granule都对应primary.idx里面的一行
- 默认每8192行记录主键的一行值, primary.idx需要被全部加载到内存里面,其里每一行数据称为一个index mark
- 每个列都有一个mark文件,其保存的是每个granules的物理地址
- mark文件每一行保存两个地址:block_offset(定位granule的压缩数据在物理文件中的位置)granule offset(定位一个granule在解压之后的block中的位置)
缺陷
- 数据按照key的顺序做排序,因此只有第一个key的过滤效果好,后面的key过滤效果依赖第一个key的基数大小
7.查询优化
- secondary index:在URL列上构建二级索引
- 构建多个主键索引:再建一个表,用需要优化的字段作为主键第一位(数据同步两份);建一个物化视图(数据自动同步到隐式表);用Projection(数据以列文件存在,数据自动同步到隐式表,查询自动路由最优的表)
- 小结
- 主键包含的数据顺序写入
- 主键构造一个主键索引
- 每个列构建一个稀疏索引
- 通过mark的选择让主键索引可以定位到每一列的索引
- 可以通过多种手段优化非主键列的索引
8.数据合并
多个part合并成一起的过程
- 数据的可见性:过程中,未被合并的数据对查询可见;过程后,新part可见,旧被删除
- 步骤
- 通过主键找到需要读的mark
- 切分marks,并发调度reader
- Reader通过mark block_offset得到需要读的数据文件的偏移量
- Reader通过mark granule_offset得到解压后的数据文件的偏移量
- 构建列式filter做数据过滤
4.ClickHouse应用场景
1.大宽表存储和查询
-
大宽表查询
- 建很多列,每列数据可以很方便进行更改
- 可将列涉及到过滤条件下推到存储层从而加速查询
-
动态表结构
- map中的每个key都是一列
- map中的每一列都可以单独的查询
- 使用方式同普通列,可以做任何计算
2.离线数据分析
- 数据导入
- 数据可以通过spark生成clickhouse格式的文件
- 导入到hdfs上由hive2ch导入工具完成
- 数据导入数据直接导入到各个物理节点
- 数据按列导入
- 保证查询可以及时访问已有数据
- 可按需加载需要的列
3.实时数据分析
4.memory table
- 可以减少parts数量
- 数据先缓存在内存中
- 到达一定阈值再写到磁盘
5.复杂类型查询
- bitmap索引
查询
- bitmap64类型
- lowcardinality
- 对于低基数列使用字典编码
- 减少数据存储和读写的IO使用
- 可以做运行时的压缩数据过滤 总结
- ClickHouse是标准的列存结构
- 存储设计是LSM-Tree架构
- 使用稀疏索引加速查询
- 每个列都有丰富的压缩算法和索引结构
- 基于列存设计的高效的数据处理逻辑
小结
本文主要讲述了ClickHouse整体架构,具体的实践大家还是需要下来搜集资料的
参考
- 字节跳动ClickHouse教程