这是我参与「第五届青训营 」笔记创作活动的第18天
1.数据库的基本概念
定义
数据库是结构化信息或数据的有序集合,一般以电子形式存储在计算机系统中。通常由数据库管理系统(DBMS) 来控制。在现实中,数据、DBMS及关联应用一起被称为数据库系统,通常简称为数据库。
分类
- 关系数据库: 关系型数据库是把数据以表的形式进行储存,然后再各个表之间建立关系,通过这些表之间的关系来操作不同表之间的数据。
- 非关系数据库: NOSOL 或非关系数据库,支持存储和操作非结构化及半结构化数据。相比于关系型数据库,NoSOL没有固定的表结构,且数据之间不存在表与表之间的关系,数据之间可以是独立的。
- 单机数据库: 在一台计算机上完成数据的存储和查询的数据库系统。
- 分布式数据库: 分布式数据库由位于不同站点的两个或多个文件组成。数据库可以存储在多台计算机上,位于同一个物理位置,或分散在不同的网络上
- OLTP 数据库: OLTP (Online transactional processing)数据库是一种高速分析数据库,专为多个用户执行大量事务而设计。
- OLAP 数据库: OLAP (Online analytical processing) 数据库旨在同时分析多个数据维度,帮助团队更好地理解其数据中的复杂关系
ClickHouse是一个分布式的OLAP关系型数据库
OLAP数据库
- 大量数据的读写,PB级别的存储多维分析,复杂的聚合函数
- 窗口函数,自定义UDF(User DefineFucntion)
- 离线/实时分析
SQL
优点:
- 标准化,ISO和ANSI是长期建立使用的SQL数据库标准
- 高度非过程化:不需要指明具体的实现
- 嵌入性:可以嵌入到高级语言中
- 简洁性:语法简单、易于使用
数据库架构
Parser
针对所给语句抽象出AST树
Analyzer
变量绑定、类型推导、语义检查、安全、权限检查、完整性检查等,为生成计划做准备
Optimizer
为查询生成性能最优的执行计划,进行代价评估
Executor
将执行计划翻译成可以执行的物理计划并驱动其执行
存储引擎
1.管理内存数据结构
- 索引
- 内存
- 数据缓存 Query cacheData cacheIndex cache
2.管理磁盘
- 数据磁盘数据的文件格式
- 磁盘数据的增删查改
- 读写算子数据写入逻辑数据读取逻辑
2.列式存储
两种存储的比较
| id | name | age | phone |
|---|---|---|---|
| 1 | e1 | 18 | 123-456 |
| 2 | e2 | 19 | 123-256 |
行式存储:1,e1,18,123-456
列式存储:(1,2),(e1,e2),(18,19),(123-456,123-256)
压缩算法
- 数据压缩可以使读的数据量更少,在IO密集型计算中获得更大的性能优势
- 相同类型压缩效率更高
- 排序之后压缩效率更高
- 可以针对不同类型使用不同的压缩算法
LZ4
输入: abcde_bcdefgh_abcdefghxxxxxxx
输出: abcde_(5,4)fgh_(14,5)fghxxxxxxx
LZ4使用(重复字段与当前字段位置差,重复字段长度)来标记重复字段以实现压缩
Run-length encoding
输入WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW
输出: 12W1B12W3B24W1B14W
Run-length encoding可以压缩重复的数据,也可以在压缩数据上计算
Delta encoding
输入: 105, 135, 112, 135, 143, 147 输出: 105(base) 30, -23, 23, 8, 4
Delta encoding可以通过确定基准值,以每个值和上一个之差作为输出来做计算,也可以通过特定算子在压缩数据上计算
列式存储的优点
- 适合选取特定的列而不是全部数据来做计算
- 适合进行聚合计算
- 延迟物化(物化:将列中的压缩数据转化为可以被计算或者输出的行数据或者内存数据结果的过程)
先对ab的结果过滤,物化之后的结果去过滤c,再对结果去物化,也即将物化放置到整个运行周期的末尾。
- 向量化
SIMD(single instruction multiple data),对于现代多核CPU,其都有能力用一条指令执行多条数据
以for循环为例:
for(size_t i=0;i<100;++i)
c[i]=a[i]+b[i];
c[0] = a[0]+b[0];
c[1] = a[1]+b[1];
...
当SIMD像上面两种方式处理连续内存和明确的数据类型的数据时,就可以并行计算寄存器最大存储长度数量的的数据
按照列式存储时,由于按列读取,类型一致,存储连续
3.ClickHouse存储设计
- 分布式表:不存储数据,用于将查询路由到集群的各个节点
- cluster:逻辑集群,由多个节点组成
- shard_key:指导数据写入分布式表时的分布方式
- 本地表:存储数据的表
集群架构
引擎架构
文件组织
CREATE TABLE test.test_insert_local
(
`p_date` Date,
`id` Int32
)
ENGINE = MergeTree
PARTITION BY P_date
ORDER BY id
SETTINGS index_granularity = 8192
根据上面的建表命令,会生成下列文件
part是文件夹的物理名称,partition是逻辑结构
每个column都是一个文件,所有的column都在自己的文件夹下
一个part都有一个主键索引,每个column都有列索引
索引设计
B+ Tree
- 所有的数据都存储在叶子节点,非叶子节点只保存key值
- 叶子节点维护到相邻叶子节点的引用
- 可以用个key值做二分查找,也可以通过叶子节点做顺序访问
LSM Tree(Long Structured Merge Tree)是一种为大吞吐写入场景而设计的数据结构
- 着重优化顺序写入
- 主要数据结构:SSTables、Memtable
SSTables
- Key按顺序存储到文件中,称为segment
- 包含多个segment
- 每个segment写入磁盘后都是不可更改的,新加的数据只能生成新的segment
Memtable
在内存中的数据都存在Memtable中,大多数情况下都以Binary Search Tree的形式存储,当Memtable存储达到阈值的时候,就会按顺序写入磁盘
数据查询
- 主要是通过建立稀疏(offset)的方式跳跃性地记录位置
Compaction合并操作
- Compaction指将多个segments合并成一个segments的过程
- 一般是有一个后台线程完成不同的segments写入新的segment的时候也是需要排序,形成新的segment之后,旧的segment文件就会被删除
索引实现
- 数据按照主键顺序依次做排序
- 数据被划分为 Granules,granule是数据读取的最小单元,不同的grandnule可以并行读取 每一个grannule都对应primary.idx里面的一行
-
默认每8192行数据被计入到primary中的一行,primary.idx需要被全部加载到内存里面
-
primary.idx每一行数据都成为一个index mark
-
mark文件保存的是压缩数据在物理文件中的位置(block_offset)以及解压后granule在block中位置的地址(granule_offset),这样查询数据时所有压缩数据会以block为单位解压到内存中,先根据block_offset找到文件,再根据granule_offset找到文件中的数据即可
缺陷
数据按照key的顺序做排序,因此只有第一个key的过滤效果好,后面的key过滤效果依赖第一个key的基数大小
查询优化
- 再建一个表,使用另一个key作为主键重新建表
- 建一个物化视图:通过select查询将一个表的数据写入一张隐式表,在物化时根据用户的指令来进行物化操作
- Projection:将另一个顺序的的主键序列的排序结果来存储到同一张表里,数据自动同步到隐式表,查询自动路由到最优的表中
数据合并
将数据分成若干个part,使得数据在一个part内是有序的,在不同part之间是无序的,将各个part进行合并
数据查询
- 根据主键找到需要读的mark
- 切分mark,然后并发地调动Reader进行读取
- Reader通过mark block_offset得到需要读取的数据文件的偏移量
- Reader通过mark granule_offset得到解压之后数据的偏移量
- 构建列式filter进行过滤
4.典型应用场景
大宽表查询
- 可以建非常多的列
- 可以增加,删除,清空每一列的数据
- 查询的时候引擎可以快速选择需要的列
- 可以将列涉及到的过滤条件下推到存储层从而加速查询
离线数据分析
- 数据导入
- 数据可以通过spark生成clickhouse格式的文件
- 导入到hdfs上由hive2ch导入工具完成数据导入
- 数据直接导入到各个物理节点
2.数据按列导入
- 保证查询可以及时访问已有数据
- 可以按需加载需要的列
实时数据分析
bitmap index
bitmap index中保存了一个值对应的行号,在查询时使用bitmap查询即可
总结
本节课程讲解了面向数据分析的列存数据库结构,以ClickHouse作为例子讲解了LSM-Tree等核心组件,并且使用一些实际应用场景说明了列存数据库的的高性能。