完备的核心特性+顶级架构设计,难怪ClickHouse这么秀

杨俊 2022-08-12 09:11:12

一、ClickHouse的核心特性

 

MPP (Massively Parallel Processing),即大规模并行处理,将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果(与hadoop相似)。

 

1)多个节点通过网络进行连接,协同工作,完成相同的任务(分布式存储,分布式计算)。

 

2)每个节点只访问自己的本地资源(内存、存储等)。

 

3)它是一种完全无共享(Share Nothing)结构,因而扩展能力非常好。

 

ClickHouse是一款MPP架构的列式存储数据库,吸取了其他优秀技术的精髓,将每个细节做到极致,从而在性能上远远超过其他技术

 

 

1、完备的DBMS功能

 

ClickHouse拥有DBMS完备的管理功能,但它不仅仅是一个数据库。作为DBMS,它具备以下基本功能。

 

1)DDL(数据定义语言):可以动态地创建、修改删除数据库、表和视图,而无须重启服务。

 

2)DML(数据操作语言):可以动态查询、插入、修改或者删除数据。

 

3)权限控制:可以按照用户粒度设置数据库或者表的操作权限,保障数据的安全性。

 

4)数据备份与恢复:提供了数据备份导出与导入恢复机制,满足生产环境的要求。

 

5)分布式管理:提供集群模式,能够自动管理多个数据库节点。

 

 

2、列式存储和数据压缩

 

行式存储和列式存储,数据在磁盘上的组织结构有着根本不同,数据分析计算时,行式存储需要遍历整表,列式存储只需要遍历单个列,所以列式库更适合做大宽表,用来做数据分析计算。

 

列式存储和数据压缩,是高性能数据库必不可少的重要特性。如果想让查询变得更快,最简单且最有效的方法就是减少数据扫描范围和数据传输时的大小。

 

HBase,BigTable,Cassandra,这些系统也可以单独存储单独列的值,但由于其他场景的优化,无法有效处理分析查询。在这些系统中,每秒钟可以获得大约十万行的吞吐量,但是每秒不会达到数亿行。

 

 

 

3、向量化执行引擎

 

“能升级硬件解决的问题,千万别优化程序”这句话虽然有点调侃的味道,但有时候优化程序带来的性能提升,还真不如直接升级硬件来的简单直接。硬件层面的优化确实是最直接、最高效的方式之一。

 

向量化执行,就是利用寄存器硬件层面的特性,为上层应用程序的性能带来了指数级的提升。

 

为了实现向量化执行,需要利用CPU的SIMD(Single Instruction Multiple Data,即用单条指令操作多条数据)命令,它是通过数据并行来提高性能,原理就是在CPU寄存器层面实现数据的并行操作。

 

1)CPU

 

中央处理单元(Cntral Pocessing Uit)的缩写,也叫处理器,是计算机的运算核心和控制核心。人靠大脑思考,电脑靠CPU来运算、控制。让电脑的各个部件顺利工作,起到协调和控制作用。

 

从功能方面来看,CPU的内部由寄存器,控制器,运算器和时钟四部分构成,各部分之间由电流信号相互连通。

 

  • 寄存器:可用来暂存指令,数据等处理对象,可以将其看做是内存的一种。根据种类的不同,一个CPU内部会有20~100个寄存器。

 

  • 控制器:负责把内存上的指令,数据等读入寄存器,并根据指令的执行结果来控制整个计算机。

 

  • 运算器:负责运算从内存读入寄存器的数据。

 

  • 时钟:负责发出CPU开始计时的时钟信号。不过,也有些计算机的时钟位于CPU的外部。

 

2)内存

 

  • 负责硬盘等硬件上的数据与CPU之间数据交换处理;

 

  • 缓存系统中的临时数据;

 

  • 断电后数据丢失。

 

3)CPU缓存

 

CPU缓存的定义为CPU与内存之间的临时数据交换器,它的出现是为了解决CPU运行处理速度与内存读写速度不匹配的矛盾——缓存的速度比内存的速度快得多。

 

 

三级缓存(包括L1一级缓存、L2二级缓存、L3三级缓存)都是集成在CPU内的缓存,它们的作用都是作为CPU与主内存之间的高速数据缓冲区。

 

  • L1最靠近CPU核心;L2其次;L3再次。

 

  • 运行速度方面:L1最快、L2次快、L3最慢。

 

  • 容量大小方面:L1最小、L2较大、L3最大。

 

CPU会先在最快的L1中寻找需要的数据,找不到再去找次快的L2,还找不到再去找L3,L3都没有那就只能去内存找了。

 

 

4)磁盘

 

存储资料和软件等数据的设备,有容量大、断电数据不丢失的特点。

 

典型计算机的存储层次结构如下图所示,存储媒介距离CPU越近,则访问数据的速度越快。

 

 

可以看出寄存器访问数据的速度,是内存的300倍,是磁盘的30000 万倍。

 

秒的换算:s(秒)=1000ms(毫秒)=1000 000μs(微秒)=1000 000 000ns(纳秒)=1000 000 000 000ps(皮秒)

 

 

4、支持SQL

 

ClickHouse使用关系型模型描述数据,并提供了传统数据库的概念,如数据库、表、视图和函数等。ClickHouse完全可以使用SQL作为查询语言,用户基础大,容易上手。(一个强大的技术必须惠及大众,人人都会用)

 

1)Hive

 

Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据。

 

2)Spark SQL

 

Druid 是一个分布式的数据分析平台,预聚合算是 Druid 的一个非常大的亮点,通过预聚合可以减少数据的存储以及避免查询时很多不必要的计算。

 

Apache Kylin™是一个开源的、分布式的分析型数据仓库,提供Hadoop/Spark 之上的 SQL 查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由 eBay 开发并贡献至开源社区。它能在亚秒内查询巨大的表。

 

 

5、表引擎

 

与MySQL类似,ClickHouse将存储部分进行了抽象,把存储引擎作为一层独立的接口。ClickHouse目前拥有合并树、内存、文件、接口和其他6大类等20多种表引擎,用户可以根据实际应用场景,选择合适的表引擎使用。

 

一个通用的表引擎可以有更广泛的适用性,能适应更多的应用场景,但是正是这种通用性,造成它无法在所有应用场景内性能做到极致,也是一种平庸的表现。

 

举个导弹的例子:短程,中程,远程洲际导弹

 

将表引擎独立设计,可以通过特定的表引擎支撑特定的场景,用法十分灵活。对于简单的应用场景,可以直接使用简单的引擎降低成本,而复杂的应用场景也有合适的表引擎。

 

 

6、多线程和分布式

 

向量化执行是通过数据级并行的方式提升了性能,多线程处理是通过线程级并行的方式实现了性能的提升。

 

ClickHouse在数据存取方面,既支持分区(纵向扩展,利用多线程技术),也支持分片(横向扩展,利用分布式技术)。ClickHouse将多线程和分布式的技术应用到了极致。

 

 

7、多主架构

 

Hadoop生态系统技术都采用了Master-Slave主从架构,由一个主节点统筹全局。而ClickHouse则采用Multi-Master多主架构,集群中每个节点角色对等,客户端访问任意一个节点都能得到相同的效果。

 

多主架构有很多优势:

 

  • 对等的角色使用系统架构变得更加简单

 

  • 不再区分主控节点、数据节点和计算节点

 

  • 集群中的所有节点功能相同

 

ClickHouse规避了单点故障的问题,非常适合用于多数据中心、异地多活的场景。

 

 

8、实时查询

 

ClickHouse支持实时查询,即便是在复杂查询的场景下,也能够做到极快响应,且无需对数据进行任何预处理加工。

 

与其他技术相比,ClickHouse优势十分明显:

 

1)Vertica商用软件价格高昂。

 

Vertica(不开源)是一款基于列存储的MPP (massively parallel processing)架构的数据库。它可以支持存放多至PB(Petabyte)级别的结构化数据。Vertica是由关系数据库大师Michael Stonebraker(2014 年图灵奖获得者)所创建,于2011年被惠普收购并成为其核心大数据平台软件。

 

2)SparkSQL和Hive无法保障90%的查询在1秒内容返回,在海量数据的复杂查询可能需要分钟级别的响应时间。

 

3)ElasticSearch在处理亿级数据聚合查询的时候,会显得力不从心。

 

 

9、数据分片和分布式查询

 

ClickHouse支持分片,也是采用分治思想,将数据进行横向切分,解决存储和查询的瓶颈。ClickHouse分片依赖集群,每个集群由1个到多个分片组成,而每个分片则对应ClickHouse的1台服务器节点,分片的数量取决于节点数量。(一个分片只能对应一台服务器节点)

 

ClickHouse的分片功能没有那么自动化,它提供了一个本地表(Local Table)和分布式表(Distribute Table)的概念。

 

1)一张本地表等同于一份数据分片。

 

2)分布式表本身不存储任何数据,它是本地表的访问代理,作用类似分库中间件。借助分布式表,能够代理访问多个数据分片,从而实现分布式查询。

 

这种设计类似数据库的分库与分表,使用非常灵活。

 

在业务系统上线的早期,数据量不大,此时数据无需分片,所以使用单节点的本地表即可满足业务需求。

 

随着业务增长,数据量增大,再通过新增分片的方式分流数据,通过分布式表实现分布式查询。

 

二、ClickHouse架构设计

 

 

 

1、Column和Field

 

Column和Field是ClickHouse数据最基础的映射单元。ClickHouse按列存储数据,内存中的一列数据由一个Column对象表示。在大多数情况下,ClickHouse都会以整列的方式操作数据,但如果需要操作某列中单个具体的数值,就需要使用Field对象,Field对象代表一个单值。

 

Column对象采用泛化设计思路,对象分为接口和实现两个部分。接口定义了对数据进行各种关系运算的方法;而这些方法的具体实现,根据数据类型的不同,由相应的对象实现。

 

Field对象使用了聚合的设计模式,在Field对象内部聚合了Null、UInt64、String和Array等多种数据类型和对应的处理逻辑。

 

 

2、DataType

 

数据的序列化和反序列化工作由DataType负责,虽然DataType负责序列化相关工作,但DataType并不直接负责数据的读取,而是转由从Column或者Field对象获取数据。

 

虽然Column和Field组成了数据的基本映射单元,但在实际操作中还缺少一些必要信息,如数据的类型和列的名称,于是ClickHouse设计了Block对象,ClickHouse内部的数据操作是面向Block对象进行的,Block对象可以看作是数据表的子集。

 

Block对象的本质是由数据对象(Column)、数据类型(DataType)和列名称(列名称字符串)组成的三元组。Column提供了数据的读取能力,DataType提供了序列化和反序列化,Block在这些对象的基础上实现了进一步的抽象和封装,从而简化了整个使用的过程,仅通过Block对象就能完成一系列的数据操作。

 

 

3、Function

 

ClickHouse主要提供两类函数:普通函数和聚合函数。

 

1)普通函数

 

由IFunction接口定义,内部有很多函数的实现,如FunctionFormatDateTime,FunctionSubstring等。普通函数是没有状态的,函数效果作用于每行数据之上,在函数执行过程中,并不会逐行计算,而是采用向量化的方式直接作用与一整列数据。

 

2)聚合函数

 

由IAggregateFunction接口定义,聚合函数是有状态的。聚合函数的状态支持序列化与反序列化,所以能够在分布式节点之间进行传输,从而实现增量计算。

 

 

4、Storage

 

ClickHouse在数据表的底层设计中并没有所谓的Table对象,它直接使用IStorage接口指代数据表。

 

表引擎是ClickHouse的一个显著特性,不同的表引擎由不同的子类实现,例如IStorageSystemOneBlock(系统表引擎),StorageMergeTree(合并树表引擎)和StorageTinyLog(日志表引擎)等。

 

IStorage接口定义了DDL(如alter、rename、drop等)、read和write方法,分别负责数据的定义、查询与写入。在数据查询时,IStorage负责根据AST(抽象语法树)查询语句的要求,返回指定列的原始数据。

 

 

5、Parser和Intercepter

 

Parser和Intercepter是非常重要的两组接口,Parser分析器负责创建AST对象;Intercepter解释器负责解释AST,并进一步创建查询的执行管道。它们与IStorage一起串联了整个数据查询过程。

 

1)Parser分析器可以将一条SQL语句解析成AST语法树的形式,不同的SQL语句,会由不同的Parser实现类解析。

 

2)Intercepter解释器对AST进行解释,然后创建查询的执行通道。

 

3)IStorage负责根据AST查询语句的指示要求,返回指定列的原始数据。

 

4)通过Block对象完成一系列的数据操作。

 

 

6、Server

 

服务器实现了多个不同的接口:

 

  • 一个用于任何外部客户端的 HTTP 接口。

 

  • 一个用于本机 ClickHouse 客户端以及在分布式查询执行中跨服务器通信的 TCP 接口。

 

  • 一个用于传输数据以进行拷贝的接口。

 

整个流程:

 

1)ClickHouse Server负责接收客户端提交的SQL查询语句。

 

2)然后交给Parser将SQL语句解析成AST语法树。

 

3)Intercepter解释器负责解释AST,并进一步创建查询的执行管道。

 

4)IStorage负责根据AST(抽象语法树)查询语句的要求,返回指定列的原始数据。

 

5)ClickHouse内部的数据操作是面向Block对象进行的(由数据对象(Column)、数据类型(DataType)和列名称(列名称字符串)组成的三元组)。

 

6)Column提供了数据的读取能力,DataType提供了序列化和反序列化。

 

 

7、Data Streams

 

ClickHouse集群中,一张数据表可以有多个分片,而每个分片都拥有自己的副本。

 

1)ClickHouse的1个节点只能拥有1个分片,也就是说如果实现1分片、1副本,则至少需要部署2个服务节点。

 

2)分片只是一个逻辑概念,其物理承载还是由副本承担。

 

三、ClickHouse高性能的底层设计

 

在设计软件架构的时候,大部分技术组件都采用自上而下的设计思路,而ClickHouse则采用了自下而上的设计方式,ClickHouse就是希望能以最快的速度进行group by查询和过滤。

 

 

1、硬件才是硬道理

 

能用钱解决的问题,千万别花时间。能用硬件解决的问题,千万别优化程序。

 

首先从硬件功能层面着手设计ClickHouse,需要考虑硬件水平(CPU、内存、磁盘、网络等)和数据结构(String、HashTable、Vector等)。

 

为了让硬件的功效最大化,ClickHouse会在内存中进行group by操作,并且使用HashTable装载数据,同时非常主要CPU L3级别的缓存使用。

 

ClickHouse就是在每个环节、细节使用最优方式,逐步积累,才达到非常惊人的性能,ClickHouse在基本查询中能做到1.75亿次/秒的数据扫描性能。

 

 

2、算法是重中之重

 

在ClickHouse的底层实现中,经常面对一些应用场景,如字符串查询、数组排序等,选择合适的算法,才能实现性能的最大化。

 

在字符串搜索方面,ClickHouse针对不同的应用场景,选择不同的算法:

 

  • 对于常量字符串查询,使用volnitsky算法。

 

  • 对于非常量字符串,使用CPU的向量化执行SIMD,进行暴力优化。

 

  • 对于字符串正则匹配,使用re2和hyperscan算法。

 

总结:性能是算法选择的首要考量的指标。

 

 

3、任人唯贤,优中选优

 

除了字符串之外,其余的应用场景也类似,ClickHouse会使用最适合、最快的算法。一旦有性能强大的新的算法出现,ClickHouse会进行验证,择优保留使用。

 

 

4、具体问题,具体分析

 

针对同一个应用场景的不同情况,可以选择不同的实现方式,尽可能将性能最大化。例如去重计数函数uniqCombined函数,会根据数据量的不同选择不同的算法:

 

  • 当数据量较小的时候,会选择Array保存。

 

  • 当数据量中等的时候,会选择HashSet保存。

 

  • 当数据量很大的时候,则使用HyperLogLog算法。

 

对于文本转换,数据过滤,数据压缩,Json转换,可以使用必杀器向量化执行。

 

 

5、不断测试,持续改进

 

构建出如此强大的ClickHouse,还需要拥有一个能够持续验证、持续改进的机制。由于Yandex数据量比较大,ClickHouse经常会使用真实的数据进行测试,这一点很好地保证了测试场景的真实性。正是有如此可靠的持续集成环境,ClickHouse才能够持续测试,快速迭代,快速改进。

 

ClickHouse不是一项单一的技术,而是一种自底向上的,追求极致性能的设计思路,这就是ClickHouse如此之快的秘诀。

 

作者丨杨俊
来源丨公众号:大数据研习社(ID:dashujux)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

活动预告