《ClickHouse原理解析与应用实践》读书笔记
第1章 ClickHouse的前世今生
开篇提到了Google开启大数据处理时代的三篇论文“Google File System”、“Google MapReduce”和“Google Bigtable”,号称三驾马车。后来一度Hadoop成为大数据处理的代名词,只要谈到大数据处理,没有用到Hadoop就认为不属于大数据处理一样,虽然Hadoop生态化的属性带来了很多便利,生态内部的组件不需要重复造轮子,只需要相互借力、组合就能形成新的方案。但是生态化的另一面就是臃肿和复杂,维护成本越来越高,Hadoop在海量数据和高时效性的双重压力下,显得力不从心了。
在OLAP领域,常见的架构分为三类:
(1)ROLAP(Relation OLAP,关系型OLAP),直接使用关系模型构建,数据模型使用星型模型或者雪花模型;
(2)MOLAP(Multidimensional OLAP,多维型OLAP),使用多维数组的形式保存数据,核心思想是借助预先聚合的结果,使用空间换取时间的形式最终提升查询性能。
(3)HOLAP(Hybrid OLAP,混合架构的OLAP)
ClickHouse名称的含义:
ClickHouse=Click Stream+Data WareHouse
第2章 ClickHouse架构概述
2.1 ClickHouse的核心特性
(1)完备的DBMS功能,支持DDL、DML、权限控制、数据备份与恢复、分布式管理
(2)列式存储与数据压缩,默认使用LZ4算法压缩
(3)向量化执行引擎,为了实现向量化执行,需要利用CPU的SIMD指令,SIMD的全称是Single Instruction Multiple Data,即单条指令操作多条数据,原理是在CPU寄存器层面实现数据的并行操作。目前利用SSE4.2指令集实现向量化执行。
(4)关系模型与SQL查询,大小写敏感
(5)多样化的表引擎,拥有合并树、内存、文件、接口和其他6大类20多种表引擎
(6)多线程与分布式,计算移动比数据移动更加划算,支持分区(纵向扩展,利用多线程原理)和分片(横向扩展,利用分布式原理)
(7)多主架构,每个节点角色对等
(8)在线查询
(9)数据分片与分布式查询,数据分片是将数据进行横向切分,一种面向海量数据的场景下,解决存储和查询瓶颈的有效手段,是一种分治思想的体现。ClickHouse提供了本地表(Local Table)与分布式表(Distributed Table)的概念。一张本地表等同于一份数据的分片。而分布式表本身不存储任何数据,它是本地表的访问代理,其作用类似于分库中间件,借助分布式表,能够代理访问多个数据分片,从而实现分布式查询。
2.2 ClickHouse的架构设计
(1)Column与Field,内存中的一列数据由一个Column对象表示。如果需要操作单个具体的值,则需要使用Field对象,Field对象代表一个单值
(2)DataType,数据的序列化和反序列工作由DataType负责
(3)Block与Block流,ClickHouse内部的数据操作是面向Block对象进行的,并且采用了流的形式,Block对象可以看作数据表的子集。Block对象的本质是由数据对象、数据类型和列名称组成的三元组,即Column、DataType及列名称字符串。流操作有两组顶层接口:IBlockInputStream负责数据的读取和关系运算,IBlockOutputStream负责将数据输出到下一个环节。
(4)Table,在数据表的底层设计中并没有所谓的Table对象,它直接使用IStorage接口指代数据表。在数据查询时,IStorage负责根据AST查询语句的指示要求,返回指定列的原始数据,之后再将数据交由Interpreter做进一步处理。
(5)Parser与Interpreter,Parser分析器负责创建AST对象,而Interpreter解释器则负责解释AST,并进一步创建查询的执行管道,他们与IStorage一起,串联起整个数据查询的过程
(6)Functions与Aggregate Functions,普通函数由IFunction接口定义,在函数具体执行过程中,并不会一行一行地运算,而是采用向量化的方式直接作用于一整列数据。聚合函数由IAggregateFunction接口定义,相比无状态的普通函数,聚合函数是有状态的,聚合函数的状态支持序列化与反序列化,所以能够在分布式节点之间进行传输,以实现增量计算。
(7)Cluster与Replication,ClickHouse的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部署2个服务节点。分片只是一个逻辑概念,其物理承载还是由副本承担的。
2.3 Clickhouse为何如此之快
设计采用了自下而上的方式
(1)着眼硬件,先想后做,充分利用CPU L3的缓存
(2)算法在前,抽象在后,在字符串搜索方面,对于常量,使用Volnitsky算法,对于非常量,使用CPU的向量化执行SIMD,暴力优化;正则匹配使用re2和hyperscan算法,性能是算法选择的首要考量标准
(3)用于尝鲜,不行就换,选择最合适、最快的算法
(4)特定场景,特殊优化,根据数据量不同选择不同的算法。去重计数uniqCombined函数,当数据量较小的时候,选择Array保存,当数据量中等的时候,选择HashSet保存,当数据量很大的时候,使用HyperLogLog算法。对于数据结构比较清晰的场景,使用代码生成技术实现循环展开,以减少循环次数,SIMD被广泛应用于文本转换、数据过滤、数据解压和JSON转换
(5)持续测试,持续改进,使用真实场景的数据进行测试。
第3章 安装与部署
这部分使用RPM包安装比较简单,下载RPM包后,执行下述命令进行安装
[root@mdw clickhouse]# ls
clickhouse-client-20.3.12.112-1.el7.x86_64.rpm clickhouse-server-20.3.12.112-1.el7.x86_64.rpm
clickhouse-common-static-20.3.12.112-1.el7.x86_64.rpm clickhouse-server-common-20.3.12.112-1.el7.x86_64.rpm
[root@mdw clickhouse]# rpm -ivh *.rpm
Preparing... ################################# [100%]
Updating / installing...
1:clickhouse-server-common-20.3.12.################################# [ 25%]
2:clickhouse-common-static-20.3.12.################################# [ 50%]
3:clickhouse-server-20.3.12.112-1.e################################# [ 75%]
Create user clickhouse.clickhouse with datadir /var/lib/clickhouse
4:clickhouse-client-20.3.12.112-1.e################################# [100%]
Create user clickhouse.clickhouse with datadir /var/lib/clickhouse
启动服务端
[root@mdw clickhouse-server]# service clickhouse-server start
Start clickhouse-server service: Path to data directory in /etc/clickhouse-server/config.xml: /var/lib/clickhouse/
DONE
启动客户端
[root@mdw clickhouse-server]# clickhouse-client
ClickHouse client version 20.3.12.112.
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 20.3.12 revision 54433.
mdw :) show databases;
SHOW DATABASES
┌─name────┐
│ default │
│ system │
└─────────┘
2 rows in set. Elapsed: 0.007 sec.
目录结构说明:
(1)/etc/clickhouse-server/:服务端的配置文件目录,包括全局配置config.xml和用户配置user.xml,详细如下所示:
[root@mdw clickhouse-server]# ll /etc/clickhouse-server/
total 32
-rw-r--r-- 1 root root 22610 Jun 25 21:37 config.xml
-rw-r--r-- 1 root root 5190 Jun 25 21:37 users.xml
(2)/var/lib/clickhouse/:默认的数据存储目录,详细如下所示:
[root@mdw clickhouse-server]# ll /var/lib/clickhouse/
total 4
drwxr-x--- 2 clickhouse clickhouse 6 Jul 16 15:08 cores
drwxr-x--- 4 clickhouse clickhouse 35 Jul 16 15:08 data
drwxr-x--- 2 clickhouse clickhouse 6 Jul 16 15:08 dictionaries_lib
drwxr-x--- 2 clickhouse clickhouse 6 Jul 16 15:08 flags
drwxr-x--- 2 clickhouse clickhouse 6 Jul 16 15:08 format_schemas
drwxr-x--- 4 clickhouse clickhouse 35 Jul 16 15:08 metadata
drwxr-x--- 2 clickhouse clickhouse 41 Jul 16 15:08 preprocessed_configs
-rw-r----- 1 clickhouse clickhouse 58 Jul 16 15:08 status
drwxr-x--- 2 clickhouse clickhouse 6 Jul 16 15:08 tmp
drwxr-x--- 2 clickhouse clickhouse 6 Jul 16 15:08 user_files
(3)/var/log/clickhouse-server/:默认保存日志的目录,详细如下所示:
[root@mdw clickhouse-server]# ll /var/log/clickhouse-server/
total 68
-rw-r----- 1 clickhouse clickhouse 0 Jul 16 15:08 clickhouse-server.err.log
-rw-r----- 1 clickhouse clickhouse 61856 Jul 16 15:16 clickhouse-server.log
-rw-r----- 1 clickhouse clickhouse 568 Jul 16 15:08 stderr.log
-rw-r----- 1 clickhouse clickhouse 0 Jul 16 15:08 stdout.log
上述是使用RPM包安装的方式,也可以使用源码编译安装,自己也是折腾了好几天才编译通过,有三点经验分享:
(1)直接从github下载代码,速度受到限制,尤其下载第三方依赖包的时间会很长,自己尝试了几次,下载速度只有几k每秒,最后采用将github路径克隆到码云上的方式解决了;
(2)代码下载完成后,编译时,不要使用配置很低的虚拟机进行编译,要不然可能1~2天也无法编译完成,建议使用配置较好一点的物理机编译;
(3)clickhouse依赖的第三方源码,并没有指明具体的版本,所以在编译时,要通过git多切换几次版本,多次尝试,将clickhouse本身的代码与依赖第三方代码的版本匹配一致,才能编译成功,同时要注意,当切换了第三方源码的版本后,需要对应修改clickhouse中的CMakeList文件,要不然会出现不匹配或者文件找不到的问题。
第4章 数据定义
4.1 ClickHouse的数据类型
大致分为三类:基础类型、复合类型和特殊类型
关于数据类型的详细信息,在下面的网址上有详细的信息,这里不再赘述,只是对几个特殊的地方进行说明。
https://clickhouse.tech/docs/en/sql-reference/data-types/
(1)ClickHouse支持正无穷、负无穷和非数字的表达方式,示例如下:
mdw :) select 0.8/0
SELECT 0.8 / 0
┌─divide(0.8, 0)─┐
│ inf │
└────────────────┘
1 rows in set. Elapsed: 0.005 sec.
mdw :) select -0.8/0
SELECT -0.8 / 0
┌─divide(-0.8, 0)─┐
│ -inf │
└─────────────────┘
1 rows in set. Elapsed: 0.004 sec.
mdw :) select 0/0
SELECT 0 / 0
┌─divide(0, 0)─┐
│ nan │
└──────────────┘
1 rows in set. Elapsed: 0.003 sec.
(2)不同精度定点数之间的四则运算