这是我参与「第五届青训营 」伴学笔记创作活动的第19天
PPT: bytedance.feishu.cn/file/boxcnr…
讲师: ClickHouse
01 数据库基本概念
1 定义
数据库: 结构化信息或数据的有序集合, 一般以电子形式存储在计算机系统中. 通常由数据库管理系统(DBMS)来控制. 在现实中, 数据, DBMS, 及关联应用一起被称为数据库系统. 通常简称数据库
2 类型
关系型数据库: 关系型数据库是把数据以表的形式进行存储, 然后在各个表之间建立关系, 通过这些表之间的关系来操作之间的数据
非关系数据库: NoSQL或非关系数据库, 支持存储和操作非结构化及半结构化数据. 相比于关系型数据库, NoSQL没有固定的表结构, 且数据之间不存在表与表间的关系, 数据之间可以是独立的
单机数据库: 在一台计算机上完成数据的存储和查询的数据库系统
分布式数据库: 分布式数据库由位于不同站点的两个或多个文件组成. 数据库可以存储在多台计算机上, 位于同一物理位置, 或分散在不同的网络上
OLTP数据库: OLTP(Online transaction processing)数据库是一种高速分析数据库, 专为多个用户执行大量事务设计
OLAP数据库: OLAP(Online analytical processing)数据库旨在同时分析多个数据维度, 帮助团队更好地理解其数据中的复杂关系
3 OLAP数据库
大量数据的读写, PB级别的存储
多维分析, 复杂的聚合函数
窗口函数, 自定义UDF(User Define Function)
离线/实时分析
4 数据库架构
client -> parser -> analyzer -> optimizer↓
↑← executor ←↓
↓ ↑
storage engine (index manager, transaction manager, file manager, cache manager)
↓ ↑
disk
Parser: 词法分析, 语法分析, 生成AST树(Abstract syntax tree)
Analyzer: 变量绑定, 类型推导, 语义检查, 安全, 权限检查, 完整性检查等, 为生成计划做准备
Optimizer: 为查询生成性能最优的执行计划, 进行代价评估
Executor: 将执行计划翻译成可执行的物理计划并驱动其执行
storage engine: 存储引擎
1 管理内存数据结构
索引
内存数据
缓存(Query cache, Data cache, Index cache)
2 管理磁盘数据
磁盘数据的文件格式
磁盘数据的增删改查
3 读写算子
数据写入逻辑
数据读取逻辑
思考: 如何存储数据?
是否可以并发处理
是否可以构建索引
行村, 列存或者行列混合存储
思考: 如何读写数据?
读多写少
读少写多
点查场景
分析型场景
02 列式存储
行式存储: 按行进行连续存储
列式存储: 按列进行连续存储
1 数据压缩
优点:
数据压缩可以使读的数据量更少, 在IO密集型计算机中获得更大的性能优势
相同类型压缩效率更高
排序之后压缩效率更高
可以针对不同类型使用不同的压缩算法
压缩算法
LZ4
Run-length encoding
Delta encoding
...
2 数据选择
优点
可以选择特定的列作计算而不是读多有列
对聚合计算友好
3 延迟物化
物化: 将列数据转换为可以被计算或者输出的行数据或者内存数据结果的过程, 物化后的数据通常可以用来做数据过滤, 聚合计算, Join
不是很懂, 之后去了解了解⭐
延迟物化
尽可能推迟物化操作的发生
优点
缓存友好
CPU/内存宽带友好
可以利用到执行计划和算子的优化, 如filter
保留直接在压缩列做计算的机会
4 向量化
向量化
SIMD(single instruction multiple data): 一条指令执行多条数据
数据格式要求:
需要处理多个数据, 因此数据需要是连续内存
需要明确数据类型
执行模型要求:
数据需要按批读取
函数的调用需要明确数据类型
列存数据库适合设计出这样的执行模型, 从而使用向量化技术
按列读取
每种列类型定义数据读写逻辑
函数按列类型处理
5 行存 vs 列存
行存
优点:
数据被保存在一起
INSERT/UPDATE容易
缺点:
选择(Selection)时即使只涉及某几列, 所有数据也都会被读取
适用场景:
点查询(返回记录少, 基于索引的简单查询)
增, 删, 改操作较多的场景
列存
优点:
查询时只有涉及到的列会被读取
投影(Projection)很高效
任何列都能作为索引
便于做延迟物化和向量化计算
压缩效率高, 每一列可以使用不同的压缩算法
缺点:
选择完成时, 被选择的列需要重新组装
INSERT/UPDATE比较麻烦
点查询不适合
适用场景:
统计分析类查询(OLAP), 比如数据仓库业务, 此类型的表上会做大量的汇聚计算, 且设计的列操作较少, 关联, 分组操作较多
即时查询(查询条件不确定, 行存表扫描难以使用索引)
03 ClickHouse存储设计
分布式表: 不存储数据, 用于将查询路由到集群的各个节点
cluster: 逻辑集群, 由多个节点组成
shard_key: 指导数据写入分布式表时的分布方式
本地表: 实际存储数据的表
架构
↓ client ↑
Handler(Tcp, Http)
↑ ↓
↑ → Interpreter(TreeRewriter, Analyzer)
↑ ↓
↑ Storages(MergeTree, SelectExecutor, Readers, writers) -> disk
↑ ↓
Executor
存储架构
part和partition
part是物理文件夹的名字
partition是逻辑结构
part和column
每个column都是一个文件
所有的column文件都在自己的part文件夹下
column和index
一个part有一个主键索引
每个column都有列索引
数据结构⭐
Hash Index
1 将输入的key通过一个HashFunction映射到一组bucjet上
2 每个bucket都包含一个指向一条记录的地址
3 哈希索引在查找时只适用于等值比较
B-Tree
1 数据写入是有序的, 支持增删改查
2 每个节点有多个孩子节点
3 每个节点都按照升序排序key值
4 每个key有两个指向左右孩子节点的引用
左孩子节点保存的key都小于当前的key
右孩子节点保存的key都大于当前的key
B+Tree
1 所有的数据都存储在叶子节点, 非叶子节点只保存key值
2 叶子节点维护到相邻叶子节点的引用
3 可以通过key值做二分查找, 也可以通过叶子节点做顺序访问
问题:
对于大数据量, B(B+)-Tree深度太高
索引数据量太大, 多个列如何平衡查询和存储
OLAP场景写入量非常大, 如和优化写入
LSM-Tree(Log-structured merge-tree): 是一种大吞吐写入场景而设计的数据结构
着重优化顺序写入
主要数据结构
1 SSTables
2 Memtable
SSTables
1 Key按顺序存储到文件中, 称为segment
2 包含多个segment
3 每个segment写入磁盘后都是不可更改的, 新加的数据只能生成新的segment
Memtable
1 在内存中的数据保存在memtable中, 大多数实现都是一颗Binary search tree
2 当memtable存储的数据到达一定的阈值时, 就会按顺序写入到磁盘
LSM-Tree的数据查询
需要从最新的segment开始遍历每个key
也可以为segment建一个索引
LSM-Tree的Compaction(合并)
将多个segments合并为一个segment
索引实现
数据按照主键顺序依次做排序
数据被划分为granules
1 granules是最小的数据读取单元(不是最小写入单元)
2 不同的granulas可以并行读取
3 每个granule都对应primary.idx里面的一行
mark文件
1 mark文件保存的是每个granules的物理地址
2 每一列都有一个自己的mark文件
granule: 颗粒, 粒度
04 ClickHouse典型应用场景
大宽表存储和查询
离线数据分析
实时数据分析
总结
1 ClickHouse是标准的列存结构
2 存储设计是LSM-Tree架构
3 使用稀疏索引加速查询
4 每个列都有丰富的压缩算法和索引结构
5 基于列存设计的高效的数据处理逻辑