有时候真的挺替MySQL鸣不平的,被最广泛地应用在各个系统中,却当着Redis、ES、Oracle的背景板,挨着最狠地骂。
嗯,今天又拿它跟OLAP数据库ClickHouse进行比较了。
一般来讲,90%多的Java工程师是接触不到ClickHouse的,而用到它的最大原因,无非是拿MySQL硬抗海量数据的统计分析类的场景实在太吃力了,临战换将成ClickHouse之后,顿觉世界时如此美好。
接下来我们就来说说,ClickHouse到底有多牛逼,以及牛逼在哪儿。
对比数据
在一系列官方公布的基准测试对比中,ClickHouse都遥遥领先对手,这其中不乏一些我们耳熟能详的名字。
所有用于对比的数据库都使用了相同配置的服务器,在单个节点的情况下,对一张拥有133个字段的数据表分别在1000万、1亿和10亿三种数据体量下执行基准测试,基准测试的范围涵盖43项SQL查询。
在1亿数据集体量的情况下,ClickHouse的平均响应速度是Vertica的2.63倍、InfiniDB的17倍、MonetDB的27倍、Hive的126倍、MySQL的429倍以及Greenplum的10倍。
下图也是ClickHouse官网上公布的测试数据:
图片
接下来我们具体分析一下,ClickHouse到底具备那些特性,以至于它比MySQL的性能高出如此之多。
MPP架构
ClickHouse采用典型的MPP(Massively Parallel Processing)架构,直译为大规模并行处理架构,可将任务并行的分散到多个节点上,每个节点各自独立完成自己的计算任务,然后将各节点的处理结果进行二次加工汇总,形成最终的结果进行返回。
如下图所示:
图片
MPP架构中的各个计算节点相互独立,具备高性能和易扩展的优点,可在海量数据下实现高吞吐量和低延迟的数据处理能力。
而MySQL InnoDB本身是不具备分布式并行计算能力的,一般情况下都是用分库分表中间件进行实现的,也有少部分是直接在工程代码中进行实现。
当然,从完善度和平滑性来讲,肯定是不如ClickHouse的一体化解决方案的。
列式存储
假设这样的一个业务场景,一张有2000多万条记录的people表,我们需要计算出表中所有人的平均年龄。
如下图所示:
图片
对MySQL InnoDB存储引擎比较熟悉的同学都知道,其存储结构是按照表空间(tablespace)——>段(segment)——>区(extent)——>page(页)的方式进行组织的,而page是MySQL InnoDB的最小IO单元,默认为16k。
而page中存储的才是MySQL InnoDB的行记录(row),每行记录中有若干个列字段值。
如下图所示:
图片
那么,对于MySQL InnoDB存储引擎的行式数据库来说,如果需要计算people全表的平均年龄,那就需要以page为IO单元全表扫描出所有数据,再把里面的age数据挑出来进行计算,才能得出最终的结果。
而Clickhouse的存储形式则完全不一样了,它是以数据列的方式进行组织存储的,每个列字段都拥有独立的.bin数据文件,并以列字段的名称命名。
如下图所示:
图片
如果需要计算people全表的平均年龄的话,ClickHouse只需要读取age.bin文件中的数据进行计算即可。
这样一来,假设people表中有20个字段,对于计算出表中所有人的平均年龄的需求,ClickHouse所需要读取的数据量只有MySQL InnoDB的大约1/20,那自然想性能不高都难。
我们来看一下,ClickHouse官网对于行式存储和列式存储的对比配图,还是比较形象的。
行式存储:
图片
列式存储:
图片
数据压缩
由于压缩后的数据不仅可以节省存储空间,还可以减少磁盘IO和网络传输的数据量,这是高性能数据库必不可少的特性。
并且,ClickHouse列式数据库比MySQL InnoDB存储引擎的行式数据库,对数据压缩更加友好。
原因在于,在ClickHouse最常用的MergeTree表引擎中,数据表中的同一列数据是保存在一个文件里的,其拥有相同的数据类型和业务语义,重复项的可能性自然就很高。比如:订单金额、个人年龄、工作收入等。
而重复项越高,文件压缩率和数据体量就越小,也就越能减少磁盘IO和网络传输的压力。
ClickHouse默认使用LZ4算法进行数据压缩的,压缩比可以达到8:1。
再来说说MySQL InnoDB,可以通过如下SQL语句进行数据压缩,但压缩效果一般,仅可以节省30%到50%的磁盘空间。
ALTER TABLE sbtest1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8
并且通过性能测试得知,数据压缩对数据库服务器的负载和CPU使用率影响较大,在性能上也并无提升。
这样看来,MySQL InnoDB的数据压缩仅能节省有限的磁盘空间,效果比较鸡肋。
向量化执行引擎
像Flink、Spark、Storm这些分布式计算框架,会将一个大任务拆解成若干个可以并行执行的小任务,以此来提升整体的任务处理吞吐量。
而ClickHouse为了最大限度地将CPU的性能压榨到极致,实现了向量化执行引擎,其核心思想是充分利用现代处理器的并行处理能力,来提升代码执行效率。
向量化执行引擎可以对一组数据执行相同的一个指令,在访问速度最快的寄存器层面实现单指令、多数据(SIMD )操作,以实现空间上的并行。
各硬件访问速度对比,如下图:
图片
SIMD操作方式,如下图:
图片
索引设计
ClickHouse与MySQL InnoDB的索引设计方式截然不同,它的一级索引是采用稀疏索引的方式进行实现的。
对比如下:
图片
在上图左侧的稠密索引中,索引标记和数据记录是一一对应的,而图右侧的稀疏索引则对应的是包含N行记录的一个数据块。
稀疏索引的优点是,仅需要少量索引标记就可以记录海量数据的区间位置信息。如果索引粒度是默认的8192,那一亿条数据仅需要对应12208个索引标记,这样就可以将索引标记放到内存中,可以起到提升查询性能的效果。
在二级索引方面,MySQL InnoDB只支持B+ Tree和Hash两种类型,而ClickHouse则支持minmax、set、bloom_filter、ngrambf_v1、tokenbf_v1和inverted等索引类型。
minmax索引,用来记录N(N = granularity)个数据块内的最大值和最小值,在对某列数据进行范围查询的时候,可以过滤掉不满足条件的数据区间,其适用于数据区分度比较高的场景。
如下图所示:
图片
set索引,用来记录每个数据块中的不重复值,举个例子,如果该数据块所对应的列中有8000个1,190个2,1个3和1个4,那set中所记录的值就是(1,2,3,4)。
set索引的适用场景为,在区分度低的列上查找列值很少的数据行。比如:在订单表中查询状态为“退款”的订单。
bloom_filter索引,顾名思义,是用来构建布隆过滤器的,默认有0.025(可调整)的误差率,会将原本不存在的值误认为已存在。但用在二级索引上是不影响正确性的,仅仅是多查询了一些数据块而已。