Hadoop
生态所取代Hive
为代表的大数据技术所取代Spark
、Impala
、Kylin
等百花齐放IT
系统在早期的建设过程中多呈烟囱式发展,数据散落在各个独立的系统之内,相互割裂、互不相通解决数据孤岛
的问题,人们提出了数据仓库的概念。即通过引入一个专门用于分析类场景的数据库,将分散的数据统一汇聚到一处。借助数据仓库的概念,用户第一次拥有了站在企业全局鸟瞰一切数据的视角BI
系统称为联机分析(OLAP
)系统SaaS
模式的兴起,为传统企业软件系统的商业模式带来了新的思路,这是一次新的技术普惠SaaS
模式将之前只服务于中大型企业的软件系统放到了互联网,扩展了它的受众;2003年起,
Hadoop
生态由此开始一发不可收拾,数据分析开启了新纪元。从某种角度来看,以使用Hadoop
生态为代表的这类非传统关系型数据库技术所实现的BI
系统,可以称为现代BI
系统
BI
系统的典型应用场景是多维分析,某些时候可以直接使用OLAP
指代这类场景。)OLAP
名为联机分析,又可以称为多维分析,是由关系型数据库之父埃德加·科德(EdgarFrankCodd
)于1993年提出的概念。顾名思义,它指的是通过多种不同的维度审视数据,进行深层次分析ROLAP
性能问题。MOLAP
使用多维数组的形式保存数据,其核心思想是借助预先聚合结果,使用空间换取时间的形式最终提升查询性能ROLAP
和MOLAP
两者的集成OLAP
主要基于以Oracle
、MySQL
为代表的一众关系型数据实现。在这个时期,不论是ROLAP
还是MOLAP
,在数据体量大、维度数目多的情况下都存在严重的性能问题ROLAP
和MOLAP
。以ROLAP
架构为例,传统关系型数据库就被Hive
和SparkSQL
这类新兴技术所取代MOLAP
架构已经能够在亿万级数据的体量下,实现毫秒级的查询响应时间。尽管如此,MOLAP
架构依然存在维度爆炸、数据同步实时性不高的问题SaaS
属性的BI
分析类产品Excel
表格,上至数亿级别的企业数据,都能够在系统内部被直接处理IT
人员,通过简单拖拽或搜索维度,就能完成初步的分析查询ROLAP
、在线实时查询、完整的DBMS
、列式存储、不需要任何数据预处理、支持批量更新、拥有非常完善的SQL
支持和函数、支持高可用、不依赖Hadoop
复杂生态、开箱即用等许多特点SQL
查询。在1亿数据集体量的情况下,ClickHouse
的平均响应速度是Vertica
的2.63倍、InfiniDB
的17倍、MonetDB
的27倍、Hive
的126倍、MySQL
的429倍以及Greenplum
的10倍详细测试结果:
https://clickhouse.yandex/benchmark.html
ClickHouse
背后的研发团队是来自俄罗斯的Yandex
公司。这是一家俄罗斯本土的互联网企业,于2011年在纳斯达克上市,它的核心产品是搜索引擎。根据最新的数据显示,Yandex
占据了本国47%以上的搜索市场,是现今世界上最大的俄语搜索引擎ClickHouse
就是在这样的产品背景下诞生的,伴随着Yandex.Metrica
业务的发展,其底层架构历经四个阶段Yandex.Metrica
研发团队以OLAPServer
为基础进一步完善,以实现一个完备的数据库管理系统(DBMS
)为目标,最终打造出了ClickHouse
,并于2016年开源
ClickHouse
的发展历程
click
(点击),会产生一个event
(事件)。至此,整个系统的逻辑就十分清晰了,那就是基于页面的点击事件流,面向数据仓库进行OLAP
分析。所以ClickHouse
的全称是ClickStream
,DataWareHouse
,简称ClickHouse
ClickHouse
做到了90%的查询都能够在1秒内返回的惊人之举ClickHouse
非常适用于商业智能领域(也就是我们所说的BI
领域),除此之外,它也能够被广泛应用于广告流量、Web
、App
流量、电信、金融、电子商务、信息安全、网络游戏、物联网等众多其他领域ClickHouse
当作KeyValue
数据库使用ClickHouse
官网的案例介绍(https://clickhouse.yandex
/)
Yandex.Metrica
目前已经成为世界第三大Web
流量分析平台,每天处理超过200
亿个跟踪事件。能够拥有如此惊人的体量,在它背后提供支撑的ClickHouse
功不可没。ClickHouse
已经为Yandex.Metrica
存储了超过20万亿行的数据,90%的自定义查询能够在1秒内返回,其集群规模也超过了400台服务器
ClickHouse
更像一款“传统”MPP
架构的数据库,它没有采用Hadoop
生态中常用的主从架构,而是使用了多主对等网络结构,同时它也是基于关系模型的ROLAP
方案
ClickHouse
是一款MPP
架构的列式存储数据库
DDL
(数据定义语言)DML
(数据操作语言)n
杯果汁,非向量化执行的方式是用1
台榨汁机重复循环制作n
次,而向量化执行的方式是用n
台榨汁机只执行1
次CPU
的SIMD
指令。SIMD
的全称是SingleInstructionMultipleData
,即用单条指令操作多条数据。现代计算机系统概念中,它是通过数据并行以提高性能的一种实现方式(其他的还有指令级并行和线程级并行),它的原理是在CPU
寄存器层面实现数据的并行操作CPU
越近,则访问数据的速度越快图21 距离
CPU
越远,数据的访问速度越慢
CPU
向量化执行的特性,对于程序的性能提升意义非凡ClickHouse
目前利用SSE
4.2指令集实现向量化执行ClickHouse
完全使用SQL
作为查询语言(支持GROUPBY
、ORDERBY
、JOIN
、IN
等大部分标准SQL
),这使得它平易近人,容易理解和学习SQL
解析方面,ClickHouse
是大小写敏感的,这意味着SELECTa
和SELECTA
所代表的语义是不同的ClickHouse
使用了关系模型,所以将构建在传统关系型数据库或数据仓库之上的系统迁移到ClickHouse
的成本会变得更低ClickHouse
共拥有合并树、内存、文件、接口和其他6大类20多种表引擎。其中每一种表引擎都有着各自的特点,用户可以根据实际业务场景的要求,选择合适的表引擎使用SIMD
不适合用于带有较多分支判断的场景,ClickHouse
也大量使用了多线程技术以实现提速,以此和向量化执行形成互补ClickHouse
在数据存取方面,既支持分区(纵向扩展,利用多线程原理),也支持分片(横向扩展,利用分布式原理),可以说是将多线程和分布式的技术应用到了极致HDFS
、Spark
、HBase
和Elasticsearch
这类分布式系统,都采用了MasterSlave
主从架构,由一个管控节点作为Leader
统筹全局。而ClickHouse
则采用MultiMaster
多主架构,集群中的每个节点角色对等,客户端访问任意一个节点都能得到相同的效果。这种多主的架构有许多优势,例如对等的角色使系统架构变得更加简单,不用再区分主控节点、数据节点和计算节点,集群中的所有节点功能相同。所以它天然规避了单点故障的问题,非常适合用于多数据中心、异地多活的场景ClickHouse
经常会被拿来与其他的分析型数据库作对比,比如Vertica
、SparkSQL
、Hive
和Elasticsearch
等,它与这些数据库确实存在许多相似之处。例如,它们都可以支撑海量数据的查询场景,都拥有分布式架构,都支持列存、数据分片、计算下推等特性。这其实也侧面说明了ClickHouse
在设计上确实吸取了各路奇技淫巧。与其他数据库相比,ClickHouse
也拥有明显的优势。Vertica
这类商用软件价格高昂SparkSQL
与Hive
这类系统无法保障90%的查询在1秒内返回,在大数据量下的复杂查询可能会需要分钟级的响应时间;Elasticsearch
这类搜索引擎在处理亿级数据聚合查询时则显得捉襟见肘
ClickHouse
的“广告词”所言,其他的开源系统太慢,商用的系统太贵,只有Clickouse
在成本与性能之间做到了良好平衡,即又快又开源
ClickHouse
支持分片,而分片则依赖集群。每个集群由1到多个分片组成,而每个分片则对应了ClickHouse
的1个服务节点。分片的数量上限取决于节点数量(1个分片只能对应1个服务节点)ClickHouse
提供了本地表(LocalTable
)与分布式表(DistributedTable
)的概念。一张本地表等同于一份数据的分片。而分布式表本身不存储任何数据,它是本地表的访问代理,其作用类似分库中间件。借助分布式表,能够代理访问多个数据分片,从而实现分布式查询Column
和Field
是ClickHouse
数据最基础的映射单元ClickHouse
按列存储数据,内存中的一列数据由一个Column
对象表示Field
对象,Field
对象代表一个单值Field
对象内部聚合了Null
、UInt
64、String
和Array
等13种数据类型及相应的处理逻辑图22
ClickHouse
架构设计中的核心模块
DataType
负责。IDataType
接口定义了许多正反序列化的方法,它们成对出现,例如serializeBinary
和deserializeBinary
DataType
的实现类中,聚合了相应数据类型的Column
对象和Field
对象Block
对象的本质是由数据对象、数据类型和列名称组成的三元组,即Column
、DataType
及列名称字符串Block
流的设计就是水到渠成的事情了。流操作有两组顶层接口:IBlockInputStream
负责数据的读取和关系运算,IBlockOutputStream
负责将数据输出到下一环节Table
对象,它直接使用IStorage
接口指代数据表ClickHouse
主要提供两类函数——普通函数和聚合函数。普通函数由IFunction
接口定义,拥有数十种函数实现IAggregateFunction
接口定义,相比无状态的普通函数,聚合函数是有状态的ClickHouse
的集群由分片(Shard
)组成,而每个分片又通过副本(Replica
)组成。这种分层的概念,在一些流行的分布式系统中十分普遍。例如,在Elasticsearch
的概念中,一个索引由分片和副本组成,副本可以看作一种特殊的分片。如果一个索引由5个分片组成,副本的基数是1,那么这个索引一共会拥有10个分片(每1个分片对应1个副本)ClickHouse
的某些设计总是显得独树一帜ClickHouse
的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部署2个服务节点代码清单21 自定义集群
ch_cluster
的配置示例
ClickHouse
的设计则采用了自下而上的方式。ClickHouse
的原型系统早在2008年就诞生了,在诞生之初它并没有宏伟的规划。相反它的目的很单纯,就是希望能以最快的速度进行GROUPBY
查询和过滤CPU
、内存、硬盘、网络等ClickHouse
会在内存中进行GROUPBY
,并且使用HashTable
装载数据。与此同时,他们非常在意CPUL
3级别的缓存,因为一次L3
的缓存失效会带来70~100ns
的延迟。这意味着在单核CPU
上,它会浪费4000万次/秒
的运算;而在一个32线程的CPU
上,则可能会浪费5亿次/秒
的运算。所以别小看这些细节,一点一滴地将它们累加起来,数据是非常可观的。正因为注意了这些细节,所以ClickHouse
在基准查询中能做到1.75亿次/秒
的数据扫描性能ClickHouse
最终选择了这些算法:对于常量,使用Volnitsky
算法;对于非常量,使用CPU
的向量化执行SIMD
,暴力优化;正则匹配使用re2
和hyperscan
算法。性能是算法选择的首要考量指标ClickHouse
会使用最合适、最快的算法。如果世面上出现了号称性能强大的新算法,ClickHouse
团队会立即将其纳入并进行验证SIMD
被广泛地应用于文本转换、数据过滤、数据解压和JSON
转换等场景。相较于单纯地使用CPU
,利用寄存器暴力优化也算是一种降维打击了ClickHouse
的安装显得尤为简单,它自成一体,在单节点的情况下不需要额外的系统依赖ClickHouse
的底层访问接口支持TCP
和HTTP
两种协议,其中,TCP
协议拥有更好的性能,其默认端口为9000ClickHouse
,更为推荐的方式是通过CLI
和JDBC
这些封装接口,因为它们更加简单易用CLI
(CommandLineInterface
)即命令行接口,其底层是基于TCP
接口进行通信的,是通过clickhouseclient
脚本运行的。它拥有两种执行模式clickhouseclient
进行登录SQL
语句,相关查询结果会统一被记录到~/.clickhouseclienthistory
文件query
参数指定执行的SQL
语句multiquery
参数,它可以支持一次运行多条SQL
查询,多条查询语句之间使用分号间隔clickhouselocal
可以独立运行大部分SQL
查询,不需要依赖任何ClickHouse
的服务端程序,它可以理解成是ClickHouse
服务的单机版微内核,是一个轻量级的应用程序。clickhouselocal
只能够使用File
表引擎clickhousebenchmark
是基准测试的小工具,它可以自动运行SQL
查询,并生成相应的运行指标报告,例如执行下面的语句启动测试SQL
进行测试,此时需要将SQL
语句定义在文件中clickhousebenchmark
支持对比测试,此时需要通过此参数声明两个服务端的地址DML
查询实属难能可贵ClickHouse
支持较完备的DML
语句,包括INSERT
、SELECT
、UPDATE
和DELETE
。虽然UPDATE
和DELETE
可能存在性能问题,但这些能力的提供确实丰富了各位架构师手中的筹码,在架构设计时也能多几个选择DBMS
(数据库管理系统),ClickHouse
提供了DDL
与DML
的功能,并支持大部分标准的SQL
。也正因如此,ClickHouse
十分容易入门OLAP
数据库黑马,ClickHouse
有着属于自己的设计目标,高性能才是它的根本,所以也不能完全以对传统数据库的理解度之。比如,ClickHouse
在基础数据类型方面,虽然相比常规数据库更为精练,但同时它又提供了实用的复合数据类型,而这些是常规数据库所不具备的ClickHouse
提供了许多数据类型,它们可以划分为基础类型、复合类型和特殊类型Int
类型Int
8Int
16Int
32Int
64其末尾的数字正好表明了占用字节的大小(8位=1字节)
ClickHouse
的浮点数CPU
,所以Decimal
128是在软件层面模拟实现的,它的速度会明显慢于Decimal
32与Decimal
64String
FixedString
UUID
DateTime
、DateTime
64和Date
三类。ClickHouse
目前没有时间戳类型。时间类型最高的精度是秒,也就是说,如果需要处理毫秒、微秒等大于秒分辨率的时间,则只能借助UInt
类型实现DateTime
64可以记录亚秒,它在DateTime
之上增加了精度的设置n
个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容Nullable
并不能算是一种独立的数据类型,它更像是一种辅助的修饰符,需要与基础数据类型一起搭配使用Nullable
类型的时候还有两点值得注意Nullable
类型,包括Nullable
的数据表,不然会使查询和写入性能变慢。因为在正常情况下,每个列字段的数据会被存储在对应的[Column].bin
文件中。如果一个列字段被Nullable
类型修饰后,会额外生成一个[Column].null.bin
文件专门保存它的Null
值。这意味着在读取和写入数据时,需要一倍的额外文件操作IPv4
IPv6
IPv
4类型是基于UInt
32封装的Ordinary
:默认引擎,在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以使用任意类型的表引擎Dictionary
:字典引擎Memory
:内存引擎,用于存放临时数据Lazy
:日志引擎,此类数据库下只能使用Log
系列的表引擎MySQL
:MySQL
引擎,此类数据库下会自动拉取远端MySQL
中的数据,并为它们创建MySQL
表引擎的数据表ClickHouse
便会在安装路径下创建DB_TEST
数据库的文件目录metadata
路径下也会一同创建用于恢复数据库的DB_TEST.sql
文件ClickHouse
目前提供了三种最基本的建表方法db_name
.]参数可以为数据表指定数据库,如果不指定此参数,则默认会使用default
数据库SELECT
子句的形式创建SELECT
子句建立相应的表结构,同时还会将SELECT
子句查询的数据顺带写入DEFAULT
、MATERIALIZED
和ALIAS
ClickHouse
会根据默认值进行类型推断DEFAULT
类型的字段可以出现在INSERT
语句中。而MATERIALIZED
和ALIAS
都不能被显式赋值,它们只能依靠计算取值DEFAULT
类型的字段可以通过SELECT
返回。而MATERIALIZED
和ALIAS
类型的字段不会出现在SELECT
查询的返回结果集中DEFAULT
和MATERIALIZED
类型的字段才支持持久化。如果使用的表引擎支持物理存储(例如TinyLog
表引擎),那么这些列字段将会拥有物理存储。而ALIAS
类型的字段不支持持久化,它的取值总是需要依靠计算产生,数据不会落到磁盘ClickHouse
也有临时表的概念,创建临时表的方法是在普通表的基础之上添加TEMPORARY
关键字Memory
表引擎,如果会话结束,数据表就会被销毁;临时表的优先级是大于普通表的。当两张数据表名称相同的时候,会优先读取临时表的数据
partition
)和数据分片(shard
)是完全不同的两个概念。OLAP
数据库而言意义非凡:借助数据分区,在后续的查询过程中能够跳过不必要的数据目录,从而提升查询的性能。合理地利用分区特性,还可以变相实现数据的更新操作,因为数据分区支持删除、替换和重置操作MergeTree
)家族系列的表引擎才支持数据分区PARTITIONBY
指定分区键,例如下面的数据表partition_v
1使用了日期字段作为分区键,并将其格式化为年月的形式:system.parts
系统表,查询数据表的分区状态:partition_v
1按年月划分后,目前拥有两个数据分区,且每个分区都对应一个独立的文件目录,用于保存各自部分的数据SELECT
查询映射,起着简化查询、明晰语义的作用,对查询性能不会有任何增强POPULATE
修饰符决定了物化视图的初始化策略:如果使用了POPULATE
修饰符,那么在创建视图的过程中,会连带将源表中已存在的数据一并导入,如同执行了SELECTINTO
一般;反之,如果不使用POPULATE
修饰符,那么物化视图在创建之后是没有数据的,它只会同步在此之后被写入源表的数据。物化视图目前并不支持同步删除,如果在源表中删除了数据,物化视图的数据仍会保留SHOWTABLE
查看数据表的列表:DROPTABLE
查询MergeTree
、Merge
和Distributed
这三类表引擎支持ALTER
查询ClickHouse
内置了许多system
系统表,用于查询自身的状态信息。其中parts
系统表专门用于查询数据表的分区信息DETACH
语句卸载,分区被卸载后,它的物理数据并没有删除,而是被转移到了当前数据表目录的detached
子目录下。而装载分区则是反向操作,它能够将detached
子目录下的某个分区重新装载回去。卸载与装载这一对伴生的操作,常用于分区数据的迁移和备份场景detached
子目录,就代表它已经脱离了ClickHouse
的管理,ClickHouse
并不会主动清理这些文件。这些分区文件会一直存在,除非我们主动删除或者使用ATTACH
语句重新装载它们ClickHouse
支持集群模式,一个集群拥有1到多个节点。CREATE
、ALTER
、DROP
、RENMAE
及TRUNCATE
这些DDL
语句,都支持分布式执行DDL
语句转换成分布式执行十分简单,只需加上ONCLUSTERcluster_name
声明即可INSERT
语句支持三种语法范式,三种范式各有不同,可以根据写入的需求灵活运用VALUES
格式的常规语法:SELECT
子句形式的语法:SELECT
子句写入数据的时候,同样也支持加入表达式或函数,例如:VALUES
和SELECT
子句的形式都支持声明表达式或函数,但是表达式和函数会带来额外的性能开销,从而导致写入性能的下降。所以如果追求极致的写入性能,就应该尽可能避免使用它们ClickHouse
内部所有的数据操作都是面向Block
数据块的,所以INSERT
查询最终会将数据转换为Block
数据块。也正因如此,INSERT
语句在单个数据块的写入过程中是具有原子性的max_insert_block_size
参数在使用CLI
命令行或者INSERTSELECT
子句写入时是不生效的ClickHouse
提供了DELETE
和UPDATE
的能力,这类操作被称为Mutation
查询,它可以看作ALTER
语句的变种。虽然Mutation
能最终实现修改和删除,但不能完全以通常意义上的UPDATE
和DELETE
来理解,我们必须清醒地认识到它的不同:Mutation
语句是一种“很重”的操作,更适用于批量数据的修改和删除;Mutation
语句的执行是一个异步的后台过程,语句被提交之后就会立即返回。所以这并不代表具体逻辑已经执行完毕,它的具体执行进度需要通过
system.mutations
系统表查询
ClickHouse
启动时主动加载还是在首次查询时惰性加载由参数设置决定)加载到内存,并支持动态更新。由于字典数据常驻内存的特性,所以它非常适合保存常量或经常使用的维度表数据,以避免不必要的JOIN
查询。