在正式开始clickhouse探秘前,咱们先抛出一个问题:影响OLAP查问速度的是什么?是优良的算法么?不可否认,算法对查问性能的影响十分大,但到了现阶段通用的算法基本上曾经可能达到很高的性能了。因而,在现阶段,制约着大数据OLAP查问速度的曾经不再是算法了。那么这个问题的答案就跃然纸上了——磁盘io。上面咱们从定性和定量两个层面来剖析一下。

定性

古代计算机体系结构中,将存储系统依照层次结构顺序排列,称为存储器层次结构(Memory Hierarchy)。由此,从磁盘上读取数据的工夫远大于CPU计算的工夫。能够做个粗略的计算比照:

假如一台2.4GHz的CPU+7200转的机械硬盘。那么CPU执行一条指令的工夫是1/2.4G≈0.41ns。7200转机械硬盘的读取约为100MB每秒。也就是说CPU执行一条指令的工夫硬盘实践上只能读取约0.041个字节。而实际上0.41ns都不足以使得磁盘开始转动。

由此咱们能够晓得,CPU参加运算的工夫远小于磁盘IO的工夫。这种状况下,应用算法优化CPU执行的指令,会显得很不划算。

定量

咱们写一段代码来模仿数据库执行SELECT max(id) From tbl_a这句语句。如果不思考后面的sql解析过程,能够简略形象成两个步骤:

  1. 从磁盘中读取数据文件,载入内存
  2. 解析数据并找出最大的id
// 从磁盘将文件载入内存long s = System.nanoTime();List<String> lines = FileUtils.readLines(file,"utf8");long e1 = System.nanoTime();System.out.println("文件读取实现,耗时:"+((e1-s)/1e6)+" ms,开始找最大值");// 解析并求出最大值String maxId = lines.parallelStream()         .map(e -> e.substring(0,e.indexOf(" ")).trim())         .max(Comparator.comparingInt(Integer::parseInt)).orElse("未找到");long e2 = System.nanoTime();// 计算每个阶段的耗时System.out.println("计算实现,耗时:"+((e2-e1)/1e6)+" ms");System.out.println("程序运行实现,总耗时:"+((e2-s)/1e6)+" ms(不计算生成数据文件文件的工夫)");

代码如上所示,应用下表的构造模仿数据表:

id姓名部门名称岗位名称
1Amy大数据部部门经理
2Tom大数据部部门经理
3John大数据部部门经理

表1 模仿表构造

应用3000万条数据作为测试,数据集大小为1.1G。最终执行后果为:

文件读取实现,耗时:4971.683747 ms计算实现,耗时:432.261263 ms 

能够看出,两者差距靠近了11倍。并且,这个试验的第二步,其实CPU也有很长时间是在读取内存的。不过这个试验曾经足够阐明问题了。

很显著,古代OLAP须要想方法升高磁盘IO的工夫。

升高磁盘IO的伎俩

如表2所示,古代OLAP数据库个别应用两种伎俩升高磁盘IO工夫对查问性能的影响。

伎俩原理代表数据库缺点
分布式并行读取数据,升高单台服务器须要读取的数据量Hive(textfile),greenplum独自应用实质上仍然须要读取所有数据,消耗资源
行变列(列存数据库)行存数据库即便只须要某一列的数据,也必须读取所有数据(如定量中的例子)列存通过将每一列独自存储,做到按需读取hbase适宜应用列比拟繁多的业务。

表2 升高磁盘IO工夫的伎俩

分布式是大数据晚期时代应用的计划,他的原理和思维非常简单粗犷,单机解决慢,我就分成N台服务器进行计算,每一台只须要计算一小部分,最初将后果汇总。例如最晚期的Hive。这种架构的长处是简略粗犷,但也有显著的缺点,就是尽管等待时间变少了,然而整体上看,将多台机器的计算工夫汇总,总耗时是大于单机解决工夫的。而且也很容易产生数据歪斜问题和减少网络传输工夫。其实是节约了资源。因而随着技术的倒退,基本上大家都应用了分布式和行变列混用的架构。hive在前期也减少了列存引擎。

列存数据库的原理将每一列独自存储,升高每次载入的数据量。还是以定量分析节中的例子为例,数据文件data.bin大小为1.1G,载入工夫4.9s。如果将每一列独自写入一个文件,那么如果只须要计算出最大id,那就只须要载入id.bin文件,这个文件约为247MB,实践上能将读取工夫缩小75%左右。同样,列存也有一个很大缺点,思考一个极其状况,如果查问语句非常复杂,从而用到了所有的列,那么列存也无奈升高读取工夫。所以,OLAP适宜解决大宽表,列越多,其性能比照行式数据库的晋升越大。

这里插个题外话,理解了列存数据库的原理后,读者要明确,将数据从行式数据库导入到列式数据库进行剖析时,请肯定记得将行式数据库的星型表转换成列式数据库的大宽表。否则性能晋升有可能会不显著。在我的职业生涯中,我就遇到过数仓工程师将数据从MySQL导入adb后,间接进行查问,而后嗔怪adb性能为什么这么差!这也是为什么数仓模型要分层的起因。另外有些读者可能会有纳闷,为什么行式数据库不搞成大宽表?这外面的起因不在本系列的领域内,后续笔者会另外写一篇独自的文章,为读者讲透这外面的起因,敬请期待,也能够在评论区说出你的想法。

clickhouse就是应用了列存的数据库,相似的还有前期的hive、greenplum。那么,同样都是列存数据库,凭什么clickhouse就能比这些数据库快呢?本系列的后续局部将持续探讨clickhouse的巧思。再次强调,clickhouse并没有提出新的计算实践,只是在列存的根底上进行了多项优化。从而提现了clickhouse工程师的令人惊叹而着迷的奇思妙想。