读《ClickHouse性能之巅:从架构设计解读性能之谜》

2023-5-3|2023-5-4
wuxiaobai24
wuxiaobai24
type
status
date
slug
summary
tags
category
icon
password

第1章 数据仓库的核心技术

 
OLAP数据库也被称为数据仓库(简称数仓)。
数据库设计符合数据规范化(Normal Form, NF)的原则。简单地说,规范化是将数据表从低范式变成高范式。一般情况下,在OLTP中通常将数据规范化为第三范式(3NF)。
第一范式指表中的每个属性都不可分割,满足条件即满足第一范式。
第二范式建立在第一范式的基础上,当表中的所有属性都被主键的所有部分唯一确定,即为满足第二范式。
第三范式建立在第二范式的基础上,当表中的属性不依赖除主键外的其他属性,即为满足第三范式。
不满足第一范式的所有情况都被称为第零范式。
数据库规范化的意义在于通过规范化降低冗余,提高数据库事务性能。正是基于这个考虑,在数据库表设计中,会要求将对数据表进行规范化。
高范式的表适合事务处理,低范式的表适合分析处理。从中我们可以得出数仓建模的本质:逆规范化。数仓建模在本质上就是一个逆规范化的过程,将来自原始业务数据库的规范化数据还原为低范式的数据,用于快速分析。
ClickHouse是一个面向OLAP的数仓,很多的优化都是面向低范式数据模型的,并没有对高范式数据模型进行很好的优化。
生成DW层数据的本质即本章提到的逆规范化的过程。
Kylin的基本原理是既然复杂的多维分析查询速度慢,那就提前计算好结果并保存到HBase中,即可在需要时快速得出结果。Kylin采用将复杂计算前置的思想,降低了复杂计算的延迟。
向量化计算引擎的计算原理如图1-2所示,借助CPU提供的SIMD(Single Instruction Multiple Data,单指令多数据流)技术,可以充分发挥现代计算机体系架构的优势,最大限度地压榨单机性能。
列存储为ClickHouse带来的另一个非常明显的优势就是大幅提高了数据压缩空间。
笔者在实际项目中,基本都能做到8:1的压缩比,即8TB的数据只需要1TB的存储空间。
ClickHouse通过基于LSM(Log-Structured Merge,日志结构合并)技术的稀疏索引来应对这个挑战。通过LSM技术,保证数据写入磁盘前就已经按照要求做好了排序,这意味着数仓中非常常见的范围查询场景可以节省大量的I/O时间,从而提升查询速度。

第3章 ClickHouse架构概览

ClickHouse旗帜鲜明地选择了MPP(Massively Parallel Processing,大规模并行处理)架构,
MPP和主从架构最大的区别在于主从架构将集群中所有的服务器整合成一个单独的系统,统一对外提供服务,而MPP架构则是一个松散的集群,集群中的任意一台服务器都可以单独对外提供服务,是一个多主的结构。
列是不可变的,任何对列的操作都会产生一个全新的对象。不可变的语义使得某些对列的操作可以并行处理,从而充分利用多核处理器。
数据类型是ClickHouse进行数据处理的基础,ClickHouse引以为傲的强大的向量化计算引擎在很大程度上依赖于这套丰富的数据类型系统。
块是ClickHouse进行数据处理的基本单位。ClickHouse以块为单位对数据进行计算。
表引擎决定了数据的逻辑组成,存储引擎决定了文件的保存位置。
因此基于S3表引擎可以实现存算分离架构。
用户提交的查询任务会在计算引擎中编译优化为QueryPipeline, QueryPipeline由多个转换器(Transformer)组成,每个转换器都有其独特的功能。
ClickHouse的解析器在收到UPDATE或DELETE语句时,会创建一个异步任务,同时立即返回执行完成。需要注意的是,此时数据并未完成更新或删除,而是会在未来的某个时刻由ClickHouse的后台进程完成数据表的重建。

第4章 MergeTree存储引擎架构

 
lickHouse存储引擎的核心在于MergeTree表引擎
MergeTree中的数据一旦写入,就不能再修改。
这些事务数据库是支持数据修改的,修改的方法就是新增一条高版本的数据,在逻辑上覆盖低版本的数据,本质上是新增而不是修改
不可变原则在ClickHouse中的作用是避免数据出现竞争,从而提高并发能力。
ClickHouse中只需要考虑数据如何按行堆放。
定长数据类型在内存和文件中是首尾相接堆叠的,没有明显的定界符。
对于变长数据类型有3种堆放方式。□使用固定的分隔符。□在另一个数组中记录每个元素的长度。□将数据写入额外的数据文件,在当前数据文件中记录定长的偏移量。
ClickHouse使用第二种方式,其变长数据类型在内存中会维护两个数组:一个是存储数据的字节数组;另一个是存储元素长度的定界数组。
为了减少数据以节省磁盘空间,缩短数据的读取时间,会将数据进行压缩。数据压缩的本质是用算力换时间,ClickHouse认为在大数据中影响查询速度的主要因素在磁盘I/O上
ClickHouse将多个数据堆叠成块,将块进行压缩,实现了数据组织。
在MergeTree中,数据库、数据表和数据分区都被物化为文件夹表示,数据由一组不同类型的文件组成。
MergeTree的数据由3种文件组成,分别是数据(bin)文件、索引文件和标记文件。这3种文件是MergeTree进行读取和写入时不可缺少的文件,丢失任意一个文件,都会造成数据损坏,无法读取。除此之外,还有一些辅助文件用于校验、加速查询等功能,这类文件的丢失不会导致数据损坏,可以依据数据进行重建。
索引文件由索引和标记文件共同组成,分别对应idx和mrk后缀。
,由于ClickHouse的索引机制,导致分区带来的收益低于索引带来的收益,且两者带来的查询加速效应并不能叠加。
分区的并发效应在ClickHouse上用处不大,由于ClickHouse更倾向于单机完成数据查询,单机查询时磁盘性能已经被少数的几个查询线程占满
分区在ClickHouse上的意义更多在于对数据的管理,通过分区可以方便地对数据进行管理,而不是加速查询。
分区目录的格式为分区ID_最小数据块编号_最大数据块编号_层级
数据块编号从1开始自增,新创建的数据库最大和最小编号相同,当发生合并时会将其修改为合并的数据块编号。同时每次合并都会将层级增加1。
标记文件存储了块到文件偏移量的映射。
通过索引文件和标记文件,才能共同确定一个数据所在的文件位置。在查询时,首先通过索引确认数据所在的块,然后依据标记确认块所在的物理地址,最后通过物理地址从硬盘上读取数据。
块的大小一般在64KB~1MB之间。
ClickHouse会对数据按照表结构进行排序并写入存储设备。事务数据库则按照事务的先后顺序写入存储设备。
稠密索引
稀疏索引
控制信息是数据文件中辅助实际数据进行存储的控制信息,包括但不限于定界符、校验和、元数据、定位信息等。ClickHouse在整个数据文件中的控制信息比例非常小
ergeTree应对的是海量数据的分析与计算,实现这个目标的瓶颈在于磁盘I/O,因此需要花费更多的精力在降低磁盘I/O上。而事务数据库应对事务,需要符合ACID特性,因此存储引擎的设计更加倾向于实现事务能力,实现事务的瓶颈在于事务间的协调,而不是磁盘I/O。
预排序:在将数据写入磁盘前进行排序,以保证数据在磁盘上有序。
预排序是数据库系统中广泛使用的技术,在实现范围查找时,可以将大量的随机读转换为顺序读,从而有效提高I/O效率,缩短范围查询时的I/O时间。
ClickHouse使用修改过的LSM算法实现预排序。由于修改后LSM算法不再支持数据修改,因此降低了传统LSM算法的读放大效应,进一步缩短了磁盘I/O时间。
列存数据库非常适合存储低范式数据,供计算引擎分析。
ClickHouse的最小处理单元是块,块一般由8192行数据组成,ClickHouse进行一次压缩针对的是8192行数据,这就极大降低了CPU的压缩和解压缩时间。
3条插入语句,从而创建了3个独立的分区,因此需要在后续进行分区合并。
ClickHouse中的分区进行细分后,存在两种分区:逻辑分区和物理分区。逻辑分区是按照用户建表时所设置的规则进行的分区,物理分区是MergeTree存储引擎在实现内部算法时,不可避免地对用户逻辑分区进行物理拆分而得到的分区。
MergeTree存储引擎会在后台自动对这些散碎的物理分区进行合并,从而消除物理分区带来的影响。
MergeTree的分区合并指的是物理分区的合并
MergeTree不支持对逻辑分区进行合并。分区删除指的是对逻辑分区的删除,不支持对物理分区进行删除
MergeTree的分区合并过程是分别将两个分区的数据读取并存入内存,重新排序生成全新的物理分区再写入磁盘。旧的物理分区会在未来某个时间被MergeTree物理删除。这个过程体现了MergeTree的空间放大特性。
MergeTree的分区删除,本质上是将该逻辑分区所涉及的物理分区的文件夹直接删除。
。因此,当出现不同的数据需求时,为了同时达到最快的速度,可能需要读者创建两张表结构不同但数据相同的镜像表,以保证不同的查询任务都能获得最优的速度。下列代码分别创建了两张不同主键的表,以应对不同的查询任务。

第5章 ClickHouse计算引擎架构

 
ClickHouse将物理计划称为查询流水线(QueryPipeline)
ClickHouse计算引擎整体架构如图5-1所示,由三部分组成:SQL解析器(Parser)、解释器(Interpreter)、执行器(PipelineExecutor)。
向量化引擎和火山引擎最大的不同点在于ClickHouse的处理单元的设计是面向向量的,而传统事务数据库是面向行的。
弱。一般数据库有两阶段优化:逻辑优化和物理优化。ClickHouse只有逻辑优化,没有物理优化。
ClickHouse中实现的逻辑优化主要是基于规则进行优化,常用的手段有谓词下推、count优化、消除重复字段等。
物理优化是利用基于成本的优化器(Cost-Based Optimizer, CBO),对SQL语句的所有可能的扫描路径或连接路径进行遍历,找出代价最小的执行路径
向量化引擎的核心在于查询流水线的编排方式,火山模型和向量化模型的本质区别在于SQL语句的编排方式。
ClickHouse中的分布式表只是物理单机表的代理,并不真正存储数据,数据依然存储在单机表中。
ClickHouse的计算引擎在分布式上的性能比较弱,尤其是在遇到大表连接(Join)操作的时候,ClickHouse经常会执行失败
火山模型将SQL语句中的每一个操作都实现为一个迭代器。每个迭代器都有一个next方法,返回一个元组(Tuple)。
ClickHouse在实现火山模型时,将转换节点都放在了src/Processors/Transforms/中,约有五十个transform类。区区50个类,通过不同的堆叠方式即可实现近乎无限的复杂SQL操作。
编译模型则可以避免这个缺陷,代价就是实现和维护困难。
标准火山引擎和向量化引擎最大的不同在于每个操作节点返回的类型不同。标准火山引擎返回的是由行组成的元组,每个元组由不同的列组成;而向量化引擎返回的是由单独列组成的数组(由于也被称为列向量,因此被称为向量化引擎)。
在每个操作节点返回的都是向量的基础上,操作节点中的计算使用向量化的计算指令代替传统的循环。
ClickHouse中引用了大量支持SIMD的开源库,可以用这些开源库代替传统的实现算法。
相对于传统的火山引擎,将投影操作下推到底层可以在表中列的数量比较多的场景下获得很高的性能。
向量化引擎的实现难度在于火山模型,实现火山模型之后再进行向量化改造的难度不大。
真正的向量化引擎应该能够实现一次处理多行数据的优化。
事实上,所有的事务数据库都没有在主流版本中加入向量化支持,核心原因是应用向量化引擎是有前提条件的。
存储引擎支持是向量化引擎的基础,向量化引擎产生效果的前提是存储引擎返回的数据是按列聚集的。否则,后续对数据的计算完全可以摊销到每次投影,即使强行做了向量化操作,节省的时间相比于投影带来的时间消耗也是微不足道的。
ClickHouse提供了很多内置函数,在使用这些内置函数时会自动进行向量化优化。建议读者尽可能使用ClickHouse提供的内置函数进行计算,而不是自己写SQL语句。
(Adaptive Query Execution,自适应查询引擎)

第6章 ClickHouse与其他数仓架构的对比

 
Hive本质上是一个元数据管理平台,通过对存储于HDFS上的数据文件附加元数据,赋予HDFS上的文件以数据库表的语义,并对外提供统一的Hive SQL接口,将用户提交的SQL语句翻译为对应的MapReduce程序或Hive程序,交给相应的计算引擎执行。
Hadoop Parquet是Hadoop生态中的一种语言无关不与任何数据计算框架绑定的新型列式存储格式。
HBase中使用的是改进后的Key-Value模型,该模型的核心在于通过唯一的Key标识数据,对Value的结构没有强制要求,不像关系模型,要求同一个表的所有数据保持相同的结构
ClickHouse使用LSM算法单纯是为了预排序,且ClickHouse需要将不同分区的数据分散到对应的分区。
ClickHouse在实现LSM时,每个写入语句都会创建一个独立的子分区,由后台程序定期合并。
ClickHouse并不适合进行点查。
Kylin在架构中引入一个存储立方体(Cube)的OLAP引擎作为缓冲层,避免查询直接落到底层的Hive上
Kylin的本质是一套缓存系统,需要用户事先依据业务规则定义缓存的内容。

第7章 深度思考:决定外在能力的因素

目前业界有一些方案,是将ClickHouse单纯作为单机数据库,在ClickHouse之上建立一个新的代理,物理计划由这个代理生成并进行CBO优化。这也是一个使用结构来解决功能问题的实例。

第8章 ClickHouse使用技巧

 
CSV和TSV是等价的,两者的区别在于分隔列的字符不同。CSV使用了可见字符“,”,即ASCII码0x2C代表的字符;而TSV则使用了不可见字符制表符作为列的分隔符,即ASCII码0x09代表的字符。
使用制表符代替逗号作为列的分隔符可以避免出现二义性的问题。在大数据中,建议统一使用TSV作为导入、导出的载体。
ClickHouse要求时间列的格式必须是YYYY-MM-DD或YYYY-MM-DD HH:mm:ss。
Buffer缓冲区表引擎是一个基于内存的表引擎,其原理是在内存中创建一块区域接收插入请求,当该区域被写满后或时间达到一定程度时,再将这块内存中的数据写入磁盘的MergeTree表。
由于ClickHouse没有使用WAL(Write Ahead Log,预写日志)技术,因此系统崩溃可能导致丢失数据。
由于ClickHouse的主键或者分区键满足最左原则,因此主键的顺序决定了查询的效率。读者一定要将最频繁使用的列放在最左边。很多情况下,放在右边的列可能无法得到加速。
低基数类型(LowCardinality)是ClickHouse中的一个特殊的包装类型,通过该类型可以将数据类型进行字典编码,替换为更高效的存储格式。尤其当某一类去重后的数量少于10000时,可以大幅提高SELECT操作的效率。
物化视图和物理表类型的区别在于物化视图会自动识别底层表的变动,当底层表变动时会自动映射到物化视图中。
ClickHouse中使用了Roaring Bitmap实现稀疏位图的存储,并提供了大量的位图操作函数。读者在应用时可以考虑优先使用位图,以大幅提高性能。
变更数据捕获(Change Data Capture, CDC)是现代数据库中常用的一种实时获取数据变动的方式,用于确定和跟踪已更改的数据,以便使用更改的数据执行操作。

第10章 ClickHouse的存算分离架构

存算分离架构即存储与计算分离架构,是近几年数据库架构领域的流行架构。ClickHouse也提供了一些特性,帮助用户构建存算分离架构的ClickHouse架构。本
存算分离架构的内涵是存储与计算解耦,即不要求存储和计算在同一台服务器上执行。
在分布式系统架构中,资源调度机制从早期的事前调度,发展到如今的事后调度。事前调度指的是在系统设计初期就确定好分布式系统的并发度;事后调度则是指依据业务需求进行动态扩缩容。
在分布式系统中,事后调度基于移动数据还是移动程序,形成了两大调度机制
随着云计算技术的发展,尤其对象存储的成本越来越低,使得架构师们开始思考,能否使用云计算对象存储价格低的优势存储大数据中的海量数据,避免使用昂贵的云服务器存储数据,即平时将数据存储在价格低廉的对象存储中,在需要计算时,动态创建计算节点,完成计算任务。
存算分离架构的目的在于通过对架构的改造,使得程序可以使用Kubernetes的调度机制,从而降低云上的成本。
存算分离的典型架构有如下特点。□计算节点动态申请。□使用对象存储。□计算节点和存储之间通过网络交换数据。
存算分离架构可以大幅降低大数据系统的初始建设成本
Hadoop集群在扩容时,必须将数据进行再平衡(rebalance),否则无法从Hadoop的优化中获得收益,而再平衡操作需要消耗时间和带宽资源,存算分离架构由于计算节点不存储数据,因此动态扩缩容的消耗更小,相比较于Hadoop架构,具有更高的可扩展性。
在理想状态下,网络传输数据的时间远快于磁盘读取相同数据量数据所需的时间
存算分离有一个非常重要的前提——必须在云上构建存算分离架构。
网络效率高于磁盘效率的前提是在理想情况下,事实上这个理想情况的条件非常苛刻,与网络拓扑、存储布局、数据冗余情况、服务器硬件配置等多种因素有关。通过网络加速计算,需要非常强大的资源调度能力、运维能力,需要投入非常高的成本。而在云上的存算分离架构,这些条件由云服务商保障。
ClickHouse原生提供了将S3配置为虚拟磁盘的能力,只需要在配置文件中配置S3的地址和存储策略。
零拷贝优化指的是当复制表引擎底层存储为S3时,ClickHouse只会在两个节点上同步元数据,而不会复制数据。
在云上部署时,建议使用JuiceFS。JuiceFS的元数据存储能力可以使用云厂商提供的云服务器,实现零运维。另外,JuiceFS对文件存储进行了优化,会将文件通过三级映射,切片为4MB的块进行存储,增强了并发能力。

第11章 ClickHouse的分布式架构

ClickHouse使用多主架构实现分布式表引擎。
节点之间无共享,同步由第三方分布式协调组件提供。
在ClickHouse中,单个节点的故障最多只会引起某些数据失效,并不会将单点故障扩散到整个集群。
对于ClickHouse集群,最好选择相同的节点配置。
ClickHouse目前的任务执行逻辑不具备Shuffle能力,导致其只能使用效率比较低的Broadcast Join算法。这种算法在执行Join操作时会将其中一个表的数据广播到所有的计算节点,只有在右表数据量比较小的前提下才能达到很高的性能。
ClickHouse目前的任务执行逻辑不具备Shuffle能力,导致其只能使用效率比较低的Broadcast Join算法。这种算法在执行Join操作时会将其中一个表的数据广播到所有的计算节点,只有在右表数据量比较小的前提下才能达到很高的性能。
==join表性能差的原因?==
Shuffle Join算法
运维复杂,扩容缩容需要用户进行额外操作
ClickHouse数据由各个节点自行管理,其分布式表只是代理,不具备数据管理的能力,元数据也都分布在各个节点中。
配置逻辑集群的前提是必须配置好分布式协调组件,逻辑集群中的所有计算节点必须连接到同一个分布式协调组件之中,以实现各种数据复制、数据同步等能力
ClickHouse复制表引擎中的副本是一个真实存在的表,每一个复制表引擎中的副本都对应着ClickHouse集群中的一个物理表。这些物理表互为副本,复制表引擎会保证这些表中的数据时刻同步。
要解决单机瓶颈,需要依靠ClickHouse的分片(shard)。
复制表最大的作用在于避免数据丢失。

第12章 ClickHouse性能优化

建议按照排序键、数据结构、索引、查询的顺序进行ClickHouse性能调优。
在ClickHouse中,已经提供了原生的Date和DateTime类型,这两个类型底层使用时间戳保存数据,不需要针对时间日期进行优化。原生的时间日期类型还可以使用ClickHouse提供的多种时间日期函数,利用ClickHouse的SIMD硬件优化查询性能。用户应当尽量使用原生的时间日期类型。
lickHouse官方不建议用户使用Nullable,而是建议用户使用一个特殊意义的值配合表的默认值来处理允许为null的列。
读《检索匹配:深度学习在搜索、广告、推荐系统中的应用》CMU15-445 Project0 CppPrimer