Chapter 1
第1章 数据仓库的核心技术
现代工程界普遍认为,数据库系统可以在广义上分为联机事务处理(OnLine Transaction Process, OLTP)和联机分析处理(OnLine Analyze Process, OLAP)两种面向不同领域的数据库,其中OLAP数据库也被称为数据仓库(简称数仓)。从产品上看,有专门面向OLTP的数据库,例如MySQL、PostgreSQL、Oracle等,也有专门面向OLAP的数据库,例如Hive、Greenplum、HBase、ClickHouse等。还有一种尝试统一这两类数据库的HATP(Hybird Analyze Transaction Process)系统,例如TiDB、OceanBase等。
表1-1将OLAP和OLTP进行对比。近年来,随着技术的发展,OLAP和OLTP之间的界限也逐渐模糊,几年前OLAP并不支持事务,近几年已经出现了一些支持简单事务的OLAP引擎,ClickHouse也将简单的事务支持列入Roadmap。另外,随着分布式技术的发展,部分OLTP能够处理TB级的数据,甚至一些厂商推出的HATP数据库,直接打破了两者的界限。
表1-1 OLAP和OLTP的对比
OLAP和OLTP在功能上越来越趋于一致,使得在有些场景下OLAP和OLTP可以相互取代,这是否意味着原有分类方法失效了呢?是否未来就不再需要数仓或者不再需要事务数据库呢?ClickHouse的极致性能优化能否推动OLAP和OLTP的融合?回答这些问题需要厘清OLAP和OLTP分类的本质。
本章将从OLAP和OLTP的本质出发,向读者揭示OLAP和OLTP分类的关键因素,从而引出ClickHouse的核心理念及优化方向。
1.1 OLAP和OLTP的本质区别
OLTP在进行数据库设计时使用实体-关系模型(Entity-Relationship Model, ER模型)。在ER模型的建模过程中有一个非常重要的规范化过程。规范化的目的在于通过一系列手段使得数据库设计符合数据规范化(Normal Form, NF)的原则。简单地说,规范化是将数据表从低范式变成高范式。一般情况下,在OLTP中通常将数据规范化为第三范式(3NF)。
1.1.1 数据三范式
在规范化的过程中经常使用范式的概念,在数据库理论中有6种范式,下面挑选3种常用的范式进行简单介绍以方便读者理解后续内容。
1.第一范式
第一范式指表中的每个属性都不可分割,满足条件即满足第一范式。表1-2展示了一个不满足第一范式的例子,由于本例中的标签字还可以细分为性别、年龄、是否为VIP用户等多个属性,因此不满足第一范式。
表1-2 不满足第一范式的用户标签表
2.第二范式
第二范式建立在第一范式的基础上,当表中的所有属性都被主键的所有部分唯一确定,即为满足第二范式。表1-3展示了一个不满足第二范式的例子,本例中用户ID和标签ID组成了主键,标签名称下的两个属性只依赖于标签ID,用户所在地只依赖于用户ID,都不依赖由用户ID和标签ID组成的主键,从而不满足第二范式。删除标签名称和用户所在地即可使得表格满足第二范式。
表1-3 不满足第二范式的用户标签表
3.第三范式
第三范式建立在第二范式的基础上,当表中的属性不依赖除主键外的其他属性,即为满足第三范式。回顾表1-3,来源名称是不满足第三范式的,这是因为来源名称依赖于来源ID。如果想满足第三范式,需要将来源ID删除。表1-3经过规范化的合格数据表如表1-4、表1-5所示。
表1-4 合格的用户标签表
表1-5 合格的用户信息表
4.第零范式
不满足第一范式的所有情况都被称为第零范式。表1-2所示的是其中一种情况。数据库理论中并没有对第零范式进行严格的定义,由于作者在本书写作过程中会经常使用第零范式的模型设计,因此在本书中,如果没有特别说明,第零范式特指存在Map或数组结构的一类表。这类“第零范式”的表设计具备一定的实际意义,在作者的工作中,经常会用到这类设计。灵活应用这类第零范式,可能会收获意想不到的效果。
1.1.2 规范化的意义
一般要求在设计业务数据表时,至少设计到满足第三范式,避免出现数据冗余。从表1-3中不难发现,标签名称和来源名称是冗余的。冗余不仅增加了数据量,更重要的是,冗余会影响数据库事务,降低数据库事务性能。
表1-6展示了一个不合格的表设计,请读者关注最后两列,很明显这是不满足第三范式的一种设计。表中的最后一列“需要权限”用于设置数据权限,表中的数据意味着第一行和第三行需要admin权限才能查看。一般情况下没有问题,随着业务的变化,需要将授权级别从“2 -非公开”改为admin和manager都有权限查看。对于这种需求,如果使用表1-5的设计,就需要进行全表扫描,将数据表中所有授权级别为“2-非公开”的数据全部修改,这会严重降低数据库性能。
表1-6 影响事务性能的不合格表结构
数据库规范化的意义在于通过规范化降低冗余,提高数据库事务性能。正是基于这个考虑,在数据库表设计中,会要求将对数据表进行规范化。
1.1.3 规范化的局限
任何架构在有优势的同时,也会有局限。规范化的数据表也同样适用。规范化的数据表能够降低冗余,进而提高事务性能。同时,规范化的数据表无法支撑分析。
以表1-3~表1-5为例,表1-4和表1-5为表1-3进行规范化后的合格用户标签表。如果需要按照用户所在城市来统计年龄分布,是无法单独使用表1-4完成的。必须对表1-4和表1-5进行连接(Join)操作,得到新表才能用于分析。而在大多数数据库系统中,Join操作的过程相对于查询来说是比较慢的。
1.1.4 数仓建模的本质
通过1.1.1~1.1.3节的分析,我们可以得出一个推论:高范式的表适合事务处理,低范式的表适合分析处理。从中我们可以得出数仓建模的本质:逆规范化。数仓建模在本质上就是一个逆规范化的过程,将来自原始业务数据库的规范化数据还原为低范式的数据,用于快速分析。
在实际建模过程中,数仓经常提到的宽表本质上就是一个低范式的表。宽表将所有相关联的列全部整合到一张表中,用于后续的分析操作,这样做的好处就是所有相关信息都在这张宽表中,理论上在进行分析时就不需要进行Join操作了,可以直接进行相关的分析,提高了分析速度。这样做的缺点就是数据冗余,难以支持事务能力。
大部分数据仓库都是基于低范式数据集进行优化的,读者在使用OLAP引擎时一定要时刻记住这一点,避免将OLTP数据库中的原始高范式数据直接用于OLAP分析,否则分析效果可能会很差。正确的做法是通过逆规范化的过程将高范式数据集还原为低范式数据集,再由OLAP进行分析。
1.1.5 OLAP和OLTP的底层数据模型
OLAP和OLTP的本质区别在于底层数据模型不同。OLAP更适合使用低范式的数据表,而OLTP则更适合使用高范式的数据表。尽管它们之间的功能越来越相似,只要其底层数据模型不同,那么它们之间的区别就永远存在,结构决定功能。
ClickHouse是一个面向OLAP的数仓,很多的优化都是面向低范式数据模型的,并没有对高范式数据模型进行很好的优化。甚至在有些场景下,ClickHouse的Join能力会成为整个系统的瓶颈。
ClickHouse更适合处理低范式数据集,特别是第零范式的数据集。ClickHouse对第零范式的数据集进行了比较多的优化,在ClickHouse中灵活使用第零范式会给性能带来非常大的提升。
1.1.6 维度建模
在使用OLAP进行数据分析时,需要对原始数据进行维度建模,之后再进行分析。维度建模理论中,基于事实表和维度表构建数据仓库。在实际操作中,一般会使用ODS(Operational Data Store,运营数据存储)层、DW(Data Warehouse,数据仓库)层、ADS(Application Data Service,应用数据服务)层三级结构。
1.ODS层
ODS层一般作为业务数据库的镜像。在项目中,数仓工程师通常通过数据抽取工具(例如Sqoop、DataX等)将业务库的数据复制到数仓的ODS层,供后续建模使用。ODS层的数据结构和业务数据库保持一致,建立ODS层的原因在于,通过复制一份数据到ODS层,可以避免建模过程直接访问业务数据库,避免对业务数据库带来影响,影响线上业务。
2.DW层
将数据导入ODS层后,即可对ODS层的数据进行清洗、建模,生成DW层的数据。生成DW层数据的本质即本章提到的逆规范化的过程。由于ODS层中的数据本质上是业务数据库的副本,因此这些数据是高范式的,不适合进行OLAP分析。这也导致了在进行OLAP分析前需要将高范式的数据通过一些手段逆规范化为低范式的数据。低范式的数据作为DW层的数据,对外提供分析服务。
在进行数据逆规范化时,可能会产生一些中间结果,这些中间结果也可以存储于DW层,因此DW层有时会再次进行细分,划分成DWD(Data Warehouse Detail,数据仓库明细)层、DWM(Data Warehouse Middle,数据仓库中间)层、DWS(Data Warehouse Service,数据仓库服务)层3个更细分的层次。
ODS层的数据通过清洗后存储到DWD层,DWD层的本质是一个去除了脏数据的高质量低范式数据层。DWD层的数据通过聚合形成宽表并保存到DWM层中。DWM层已经是低范式的数据层了,可以用于OLAP分析。在某些场景中,可以对DWM层的数据进行业务聚合,以支持更复杂的业务,此时需要将生成的数据保存到DWS层中。
并不是所有场景都需要包含这3个细分的DW层。DW层的本质就是对高范式的数据进行逆规范化,生成低范式数据。读者只需要把握住这个核心,在实际的维度建模过程中,根据业务的实际需求进行建模,不需要在所有的场景下机械地遵循DWD层、DWM层、DWS层的三层架构。
本书会多次用到DW层的概念,如无特殊说明,DW层均指经过了逆规范化后的结果,而不是逆规范化的过程。
3.ADS层
ADS层保存供业务使用的数据,虽然DW层的数据可用于OLAP分析,但分析过程通常比较慢,无法支撑实时的业务需求,需要引入ADS层作为缓存,向上支撑业务。同样的,ADS层也不是必须的,需要根据业务实际情况来选择,ClickHouse的高性能计算引擎可以在一定程度上取代ADS层。
ADS层数据本质是面向业务的、高度业务化的数据,可以认为是基于DW层数据分析的结果,很多情况下是指标、标签的计算结果。本书后续提到的ADS,如无特殊说明,均指基于DW层分析后的业务化的结果。
1.2 典型大数据数仓技术及其核心思路
科学技术是不会突变的,ClickHouse也不是凭空冒出来的,而是在早年大数据数仓系统的基础上进行改良得到的。本节介绍ClickHouse诞生之前的几个知名数仓技术及其核心思路,从而引出ClickHouse的优化方向。
1.2.1 Hive
Hive是一个基于HDFS(Hadoop Distributed File System, Hadoop分布式文件系统)的数仓系统,可以将存储在HDFS之上的文件进行结构化解析,并向用户提供使用SQL操作Hadoop的能力。由于Hive构建在HDFS和Spark/MapReduce的系统之上,使得Hive具备了理论上近乎无限的大数据处理能力,因此Hive迅速在业界占据主导地位,在其巅峰时期,Hive+SparkSQL就是大数据系统的标配。Hive使用的是中间件思路,构建在Hadoop的组件之上。
Hive本质上是一个元数据系统,由于自身没有存储和计算引擎,因此Hive无法单独使用,必须配合HDFS+Spark/MapReduce,才能完成整个数仓的功能。甚至Hive的元数据存储都借助了MySQL等关系型数据库。这样的架构设计使得Hive成为事实上的Hadoop中间件系统,在大数据早期阶段具有非常重要的意义。
□降低了Hadoop的技术门槛。
□提高了Hadoop的使用效率。
□投入产出比高。
随着技术的进步,Hadoop自身的问题越来越突出,使得Hive成为整个系统的瓶颈,这时候Hive的一些问题也随之暴露,主要体现在以下几点。
□安装运维困难:安装Hive需要同时安装许多组件,易出现单点故障。
□受限于底层HDFS:如小文件问题、不支持更新等。
□受限于底层计算引擎:计算效率太低、延迟高,无法支撑即席查询等。
在ClickHouse技术尚未出现的年代,使用Hive需要同时配备非常庞大的大数据平台,这使得传统烟囱式数据开发模型由于成本原因,无法在大数据时代的早期广泛应用,因此出现了数据中台的概念。建设数据中台的一个重要原因是技术不成熟使得大数据应用的成本非常高,企业没有建设多个大数据平台的动力,需要尽可能降低建设和维护成本,这就需要将所有数据整合到“大中台小前台”的架构中,并由此诞生了一大批提供数据中台服务的公司,极大地推动了产业的发展和技术的应用。
烟囱式的数据开发模式在数据中台时代被视为洪水猛兽,应该绝对避免。当然这个观点在数据中台时代是正确的。随着大数据技术的成熟,使用全新技术的烟囱式数据开发模式其实也有着数据中台无法比拟的优势——成本低、研发快、架构灵活。这更体现出了计算机科学中的一个经典理论——没有银弹。作为架构师,应该理性、平等地分析每个架构的优点和缺点,找到最合适的架构,而不是期望找到正确的架构。
1.2.2 HBase
和Hive类似,HBase也是建立在HDFS之上的NoSQL数据库。和Hive不同的是,HBase实现了自己的存储引擎,避开了HDFS低性能的缺陷,获得了非常高的吞吐能力。凭借远比Hive高的性能,在大数据时代早期,部分场景也会将HBase当成数据仓库来使用。
HBase采用重新设计的存储引擎解决了一些数仓的技术瓶颈。HBase的存储引擎是面向NoSQL设计的,由于无法完整地实现SQL的能力,因此除了少部分不需要强大SQL能力的场景之外,HBase的使用场景非常有限。HBase的出现也有着非常重要的意义。
□证明了基于低效的HDFS也能设计出相对高性能的系统。
□弥补了Hive无法支持实时场景的缺陷。
□填补了大数据系统即席查询的空白,扩充了大数据的应用场景。
1.2.3 Kylin
Kylin是一个多维OLAP数仓。Hive的查询延迟很高,尤其在复杂的多维分析中格外明显,难以满足某些实时场景下的查询需求。HBase虽然可以解决一部分场景下的高延迟问题,但因为不支持SQL特性,所以无法支持复杂的多维分析。
Kylin就是在这样的背景下应运而生的。Kylin的基本原理是既然复杂的多维分析查询速度慢,那就提前计算好结果并保存到HBase中,即可在需要时快速得出结果。Kylin采用将复杂计算前置的思想,降低了复杂计算的延迟。从本质上看,可以认为Kylin也是一个大数据中间件。当然,Kylin也面临着如下挑战。
□维度爆炸:Kylin是将计算前置,由于很难事先预测需要组合的维度,因此只能进行穷举。这种穷举的方式面临着维度爆炸的风险。
□数据实时性弱:由于Kylin存在预计算过程,新的数据必须经过预计算才能被检索到,因此Kylin只是解决了实时查询的问题,没有解决数据无法实时响应的缺陷。
□资源浪费:由于预计算的结果并不一定会被使用,因此可能存在资源浪费的情况。
□指标逃逸:如果所需数据未被预计算,需要重新使用Spark/MapReduce进行计算。
1.2.4 其他数仓
1.2.1~1.2.3节向读者介绍了3种常用的数据仓库及其核心思路,这3种数仓分别采用了3种不同的设计思路来实现大数据的数据仓库。而其他的数仓,大多也是采用其中的一种或几种思路,例如Greenplum采用的是与Hive类似的中间件思路,只不过其底层数据库是PostgreSQL而不是Hive的Hadoop。而本书的主角ClickHouse则是采用类似HBase的思路,以极限单机性能为目标,重新设计了存储引擎和计算引擎。对于这3种数仓的架构会在第6章进行详细介绍和对比。
1.3 传统数仓的缺陷
1.2节介绍了3种典型数仓技术的核心思路,本节将对这些传统数仓的缺陷进行总结。
1.3.1 效率低
传统的数仓大多构建在Hadoop之上,这为传统的数仓带来了近乎无限的横向扩展能力,同时也造成了效率低的问题。效率低主要体现在以下几个方面。
□部署效率低:在部署Hive、HBase、Kylin之前,必须部署Hadoop集群。和传统数据库相比,这个部署效率是非常低效的。
□运维效率低:Hive、HBase、Kylin基于Hadoop, Hadoop生态会带来一个非常严重的单点故障问题,即Hadoop体系中任何一个组件出现问题,都可能引起整个系统不可用。使用传统的数仓对运维的要求非常高。
□计算效率低:主要体现在Hive和Kylin上,这两个数仓没有自己的存储引擎和计算引擎,只能依靠堆机器实现复杂查询,而无法从数据本身入手。直到在大数据时代后期,一些以数据快速查询为目标而特殊设计的数据存储格式成为标准,这个现象才有所改观。而HBase的优化核心就是重新设计的存储引擎,使得HBase可以对数据本身进行查询速度的优化。
1.3.2 延迟高
构建在Hadoop之上的数仓引擎,除了效率低的缺点之外,还面临着高延迟的挑战。高延迟主要体现在以下两个方面。
□查询延迟高:使用Hive作为数仓,受限于HDFS的性能瓶颈,Hive的查询速度比较慢,难以支撑低延迟场景,无法应用在实时计算的场景中。
□写入数据延迟高:同样受限于HDFS,Hive的数据写入延迟也很高,这意味着数据无法实时写入Hive,从而无法支撑实时分析场景。
1.3.3 成本高
传统的数仓数仓引擎还会带来成本高的挑战,主要体现在以下几个方面。
□部署成本高:由于Hadoop的计算逻辑是通过堆计算资源的方式来摊销复杂查询的时间,因此如果需要达到一个比较理想的性能,必须要求集群中节点的数量达到一定的规模,否则因为计算效率低,单机很容易成为性能瓶颈。这导致了Hive等基于Hadoop的数仓部署成本高的缺陷。
□运维成本高:集群服务器达到一定规模后,运维成本会指数级增长。同时,由于Hadoop中组件太多,任何一个组件失效都有可能导致整个服务不可用,因此运维团队必须包含所有组件的运维人员,这也极大提高了运维团队的人力成本。
□存储成本高:Hadoop的HDFS为了避免集群中因服务器故障导致的不可用情况,默认使用三副本策略存储数据,即数据会保存3份。这会极大提高存储成本。即使是新一代的Hadoop采用纠删码技术降低了副本数量,也因使用场景有限,只适合在冷数据存储中使用,对于经常需要查询的热数据,并不适合采用该方案。
□决策成本高:传统的大数据部署成本高,导致企业在做决策时面临比较大的决策成本,一方面是前期投入太大,短期内看不到效果,长期效果也很难说清楚;另一方面是即使企业下定决心来建设数仓,昂贵的基础设施和缺乏专业技术人员也会导致需要很长的建设周期,太长的建设周期又会带来很多不可预知的变数,最终影响企业的决策。
1.4 ClickHouse查询性能的优势
1.3节向读者说明了传统数仓存在的一些缺陷,本节将为读者介绍ClickHouse是如何通过精妙的存储引擎和计算引擎来解决这些问题的。
1.4.1 向量化引擎
在存储引擎的设计上,ClickHouse采用了基于列存储的存储结构。列存储在很多场景中极大地降低了数据分析过程中读取的数据量,图1-1展示了列存储相比于行存储减少数据量的原理。显然,在宽表场景下,由于行存储在抽取某些列时必须读取该行的所有列,因此读取了大量无效的数据(图1-1中行存方案中未加▲的深色方块数据为无效的不参与计算的列),从而降低了读取效率。
图1-1 列存储和行存储的对比
在计算引擎的设计上,ClickHouse首次使用了向量化计算引擎。向量化计算引擎的计算原理如图1-2所示,借助CPU提供的SIMD(Single Instruction Multiple Data,单指令多数据流)技术,可以充分发挥现代计算机体系架构的优势,最大限度地压榨单机性能。
图1-2 向量化计算引擎计算原理示意图
而ClickHouse对单机性能的压榨,使得其可以在单机部署的情况下处理大量数据。在实际使用中,基本上百亿以下的数据表,都可以使用单机解决。这种程度的单机处理能力已经可以满足非常多的企业层面的需要,也在很大程度上解决了传统大数据数仓的效率低和成本高的问题。
1.4.2 高效的数据压缩
列存储为ClickHouse带来的另一个非常明显的优势就是大幅提高了数据压缩空间。列存储是将同一列的数据存储在连续的空间中,相比于行存储,列存储在连续的空间上更有规律。而规律的存储,带来了更大的压缩率,从而大幅减少压缩后的数据大小,极大减少了磁盘的I/O时间。
笔者在实际项目中,基本都能做到8:1的压缩比,即8TB的数据只需要1TB的存储空间。这在提高计算效率的同时也降低了存储成本。相比于Hadoop的三副本策略,存储成本大幅降低。
读者可能会存在一个疑问:Hadoop的三副本能保证数据不丢失,而ClickHouse的存储是无法保证数据不丢失的,那么二者是否不能放在一起比较?这个疑问是有一定道理的,二者的应用场景不同,面临的问题也不同。Hadoop如果需要发挥能力,必须有一个庞大的集群来摊销低效率需要的额外处理时间,这意味着集群中任何一台机器出现故障,都有可能导致集群不可用,从概率学上看,假设一台机器的故障率是1%,那么100台机器中有一台出现故障的概率已经接近100%了。由此可见,在一个庞大的Hadoop集群中是必须考虑机器故障问题的。
而ClickHouse则不同,ClickHouse在设计时倾向于榨干单机性能,在很多场景下用单机解决问题。这种设计使得单机ClickHouse出现故障的概率只有1%,可以在一定程度上忽略机器故障。当然,具体场景需要读者依据业务需求进行分析,如果确实需要保证数据不丢失,可以使用RAID(Redundant Arrays of Independent Disks,磁盘阵列)在物理层面提供保障,也可以使用ClickHouse提供的复制表从软件层面解决该问题。总之,ClickHouse提供了比较灵活的机制。
1.4.3 高效的I/O优化
超高的压缩率为ClickHouse带来了更低的数据存储成本和更低的I/O时间,同时也带来了计算时的额外开销——解压。数据压缩后存储到磁盘上,意味着压缩的数据被读取后无法直接获取内容,也就无法参与分析和计算,必须经过解压还原原始数据,才可以参与分析和计算。那么如何最大限度地减少压缩时间,甚至在数据被程序读取前就过滤掉不相关的数据,成为具备压缩能力的存储引擎的一大挑战。
ClickHouse通过基于LSM(Log-Structured Merge,日志结构合并)技术的稀疏索引来应对这个挑战。通过LSM技术,保证数据写入磁盘前就已经按照要求做好了排序,这意味着数仓中非常常见的范围查询场景可以节省大量的I/O时间,从而提升查询速度。
1.5 本章小结
本章首先向读者介绍了产生OLAP和OLTP两种形态数据库的底层原因及两者的区别,并解释了为何OLAP数据库针对大宽表进行优化。然后介绍了传统的大数据数仓技术的内容及思路,并为读者分析了这些传统技术带来的缺陷。最后介绍了ClickHouse的一些优化手段。