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,简称ClickHouseClickHouse做到了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目前利用SSE4.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、UInt64、String和Array等13种数据类型及相应的处理逻辑图22
ClickHouse架构设计中的核心模块
DataType负责。IDataType接口定义了许多正反序列化的方法,它们成对出现,例如serializeBinary和deserializeBinaryDataType的实现类中,聚合了相应数据类型的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装载数据。与此同时,他们非常在意CPUL3级别的缓存,因为一次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 类型Int8Int16Int32Int64其末尾的数字正好表明了占用字节的大小(8位=1字节)
ClickHouse的浮点数CPU,所以Decimal128是在软件层面模拟实现的,它的速度会明显慢于Decimal32与Decimal64StringFixedStringUUIDDateTime、DateTime64和Date三类。ClickHouse目前没有时间戳类型。时间类型最高的精度是秒,也就是说,如果需要处理毫秒、微秒等大于秒分辨率的时间,则只能借助UInt类型实现DateTime64可以记录亚秒,它在DateTime之上增加了精度的设置n个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容Nullable并不能算是一种独立的数据类型,它更像是一种辅助的修饰符,需要与基础数据类型一起搭配使用Nullable类型的时候还有两点值得注意Nullable类型,包括Nullable的数据表,不然会使查询和写入性能变慢。因为在正常情况下,每个列字段的数据会被存储在对应的[Column].bin文件中。如果一个列字段被Nullable类型修饰后,会额外生成一个[Column].null.bin文件专门保存它的Null值。这意味着在读取和写入数据时,需要一倍的额外文件操作IPv4IPv6IPv4类型是基于UInt32封装的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和ALIASClickHouse会根据默认值进行类型推断DEFAULT类型的字段可以出现在INSERT语句中。而MATERIALIZED和ALIAS都不能被显式赋值,它们只能依靠计算取值DEFAULT类型的字段可以通过SELECT返回。而MATERIALIZED和ALIAS类型的字段不会出现在SELECT查询的返回结果集中DEFAULT和MATERIALIZED类型的字段才支持持久化。如果使用的表引擎支持物理存储(例如TinyLog表引擎),那么这些列字段将会拥有物理存储。而ALIAS类型的字段不支持持久化,它的取值总是需要依靠计算产生,数据不会落到磁盘ClickHouse也有临时表的概念,创建临时表的方法是在普通表的基础之上添加TEMPORARY关键字Memory表引擎,如果会话结束,数据表就会被销毁;临时表的优先级是大于普通表的。当两张数据表名称相同的时候,会优先读取临时表的数据
partition)和数据分片(shard)是完全不同的两个概念。OLAP数据库而言意义非凡:借助数据分区,在后续的查询过程中能够跳过不必要的数据目录,从而提升查询的性能。合理地利用分区特性,还可以变相实现数据的更新操作,因为数据分区支持删除、替换和重置操作MergeTree)家族系列的表引擎才支持数据分区PARTITIONBY指定分区键,例如下面的数据表partition_v1使用了日期字段作为分区键,并将其格式化为年月的形式:system.parts系统表,查询数据表的分区状态:partition_v1按年月划分后,目前拥有两个数据分区,且每个分区都对应一个独立的文件目录,用于保存各自部分的数据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查询。