ClickHouse - 你没有见过的列存储 | 青训营笔记

110 阅读9分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 19 天,参与青训营的第32天。今天主要学习了工作中的Redis。

01、数据库基本概念

  • OLTP数据库:OLTP(Online transactional procession)数据库是一种高速分析数据库,专为多个用户执行大量事务而设计。
  • OLAP数据库:OLAP(Online analytical procession)数据库旨在同时分析多个数据维度,帮助团队更好地理解其数据中的复杂关系

OLAP数据库

  • 大量数据的读写,PB级别的存储
  • 多维分析,复杂的聚合函数
  • 窗口函数,自定义UDF(User Define Fucntion)
  • 离线/实时分析

SQL

一种编程语言,目前几乎所有的关系型数据库都是用SQL (Structured Query Language) 编程语言来查询、操作和定义数据,进行数据访问控制。

一个简单的SQL查询包含SELECT关键词,型号(“*”)也可以用来指定查询也可以用来指定查询应当返回查询表所有字段,可选的关键词和子句。

From子句指定了选择的数据表。FROM子句也可以包含JOIN二层子句来为数据表的连接设置规则。

WHERE子句后接一个比较谓词以限制返回的行。WHERE子句仅保存返回结果里使得比较谓词的值为True的行。

Group By子句用于将若干含有相同值的行合并。

Group By通常与SQL聚合函数连用,或者用于清除数据重复的行。Group By子句要用在WHERE子句之后。

SQL的优点

1、标准化,ISO和ANSI是长期建立使用的SQL标准库标准

2、高度非过程化,用SQL进行数据操作,用户只需提出“做什么”,而不必指明“怎么做”,因此用户无需了解存取路径,存取路径的选择以及SQL语句的操作过程由系统自动完成。不但减轻用户负担,而且有利于提高数据独立性。

3、以同一种语法结构提供两种使用方式,用户可以在终端上直接输入SQL命令对数据库进行操作。作为嵌入式语言,SQL语句能够嵌入到高级语言(C,C#,JAVA)程序中,供程序员设计程序时使用。在两种不同的使用方式下,SQL的语法结构基本上是一致的。

4、语言简洁,易学致用:SQL功能极强,但由于设计巧妙,语言十分简洁、完成数据定义、数据操纵、数据控制的核心只用了9个动词:CREATE、ALTER、DROP、SELECT、INSERT、UPDATE、DELETE、GRANT、REVOKE。

数据库架构

image-20230216170203549.png

Parser:词法分析、语法分析、生成AST树(Abstract syntax tree)

Analyzer:变量绑定、类型推导、语义检查、安全、权限检查

、完整性检查等,为生成计划做准备。

Optimizer:为查询生成性能最优的执行计划,进行代价评估。

Executor:将执行计划翻译成可执行的物理计划并驱动器执行。

存储引擎

1、管理内存数据结构

  • 索引
  • 内存数据
  • 缓存
  • Querycache
  • Datecache
  • Indexcache

2、管理磁盘数据

  • 磁盘数据的文件格式
  • 磁盘数据的增删改查

3、读写算子

  • 数据写入逻辑
  • 数据读取逻辑

02、列式存储

列式存储的优点

  • 数据压缩可以使读的数据量更少,在IO密集型计算中获得更大的性能优势
  • 相同类型压缩效率更高
  • 排序之后压缩效率更高
  • 可以针对不同类型使用不同的压缩算法

Run-length encoding

  • 压缩重复的数据
  • 可以在压缩数据上直接计算

Delta encoding

  • 将数据存储为连续数据之间的差异,而不是直接存储数据本身
  • 特定算子也能直接在压缩数据上计算

数据选择:

  • 可以选择特定的列作计算而不是读所有列
  • 对聚合计算友好

延迟物化:

物化:将列数据转化为可以被计算或者输出的行数据或者内存数据结果的过程,物化后的数据通常可以用来做数据过滤,聚合计算,Join。

延迟物化:尽可能推迟吴化操作的发生。

  • 缓存友好
  • CPU/内存带宽友好
  • 可以利用到执行计划和算子的优化,例如filter
  • 保留直接在压缩列作计算的机会

向量化:

  • SIMD
  • 数据和树
  • 执行模型

SIMD(single instruction multiple data), 相对于现在多核CPU,其都有能力用一条指令执行多条数据。

SIMD程序使用的指令集有SSE合AVX系列,AVX有AVX-256和AVX-512,SSE提供128-bits的寄存器,AVX-256提供256-bits,AVX-512提供512bits的寄存器。如果这时候CPU也可以并行的计算我们写的代码,那么理论上我们的处理速度就会是之前代码的100倍,SIMD指令就可以完成这样的工作,用SIMD指令完成的代码设计和执行的逻辑就叫做向量化。

数据格式要求:

  • 需要处理多个数据,因此数据需要是连续内存
  • 需要明确数据类型

执行模型要求:

  • 数据要求按批读取
  • 函数的调用需要明确数据类型

列存数据库适合设计出这样的执行模型,从而使用向量化技术:

  • 按列读取
  • 每种列类型定义数据读写逻辑
  • 函数按列类型处理

image-20230216175502973.png

03、Click House存储设计

表定义和结构

集群架构

引擎架构

存储架构

part和partition

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

part和column

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

column和index

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

Hash index

  1. 将输入的key通过一个hashfunction映射到一组bucket上
  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深度太高
  • 索引数据量太大,多个列如何平衡查询和存储——LSM-Tree
  • OLAP场景写入量非常大,如何优化写入

Log-structured merge-tree(LSM Tree) 是一种为大吞吐写入场景而设计的数据结构

  • 着重优化顺序写入

  • 主要数据结构

    1. SSTables
    2. Memtable

SSTables

  1. Key按顺序存储到文件中,称为segment
  2. 包含多个segment
  3. 每个segment写入磁盘后都是不可更改的,新加的数据只能生成新的segment

Memtable

  • 在内存中的数据保存在memtable中,大多数实现都是一棵Binary search tree
  • 当memtable存储的数据达到一定的阈值的时候,就会按顺序写入到磁盘。

数据查询

  • 需要从最新的segment开始遍历每个key
  • 也可以每个segment键一个索引

Compaction(合并)

  • Compaction将多个segment合并成一个segments的过程
  • 一般是由一个后台线程完成
  • 不同的segments写入新的segment的时候也是需要排序,形成新的segment之后,旧的segment文件就会被删除

索引实现

数据按照主键顺序依次做排序

  • 优先按照UserID做排序
  • 再按照URL排序
  • 最后是Event Time

数据被划分为granules

  1. granules是最小的数据读取单元
  2. 不同的granules可以并行读取

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

默认每8192行记录主键的一行值,primary.idx需要被全部加载到内存里面,里面保存的每一行数据称为一个index mark,每个列都有这样一个mark文件

  • mark文件保存的是每个granules的物理地址
  • 每一列都有一个自己的mark文件

mark文件里面的每一行保存两个地址:

  1. block_offset:用于定位一个granule的压缩数据再物理文件中的位置,压缩数据会以一个block为单位解压到内存中
  2. granule_offset,用于定位一个granule在解压之后的block中的位置

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

查询优化

secondary index:在URL列上构建二级索引

构建多个主键索引

  • 再建一个表,使用需要优化的字段做主键的第一位
  • 建一个物化视图
  • 使用Projection

使用Projection

projection:类似物化视图,但是不是将数据写入新的表,而是存储在原始表中,以一个列文件的形式存在。

数据合并

数据可见性:

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

数据查询

  1. 通过主键找到需要读的mark
  2. 切分marks,然后并发的调度reader
  3. Reader通过mark block——offset得到需要读的数据文件的偏移量
  4. Reader通过mark granule_offset得到解压后数据的偏移量
  5. 构建列式filter做数据过滤

今日总结

今天从数据库出发,学习了列存储的相关特性,学习了众多优化存储数据,查询数据的技巧。