数据库-数据仓库-数据湖

举报
Smy1121 发表于 2023/01/03 11:14:29 2023/01/03
【摘要】 简述数据库、数据仓库、数据湖以及风头正劲的"Lake house" ——湖仓一体化。一、数据仓库是个啥?和数据库有什么不同?1.1 数据库       数据库的基本概念,大家应该都不陌生。如今但凡是个业务系统,都或多或少需要用到数据库。 即便我们不直接跟数据库打交道,它们也在背后默默滴为我们服务,比如刷个卡、取个钱,后台都是数据库在扛着。数据库主要用于「事务处理」,存取款这种算是最典型的,特...

简述数据库、数据仓库、数据湖以及风头正劲的"Lake house" ——湖仓一体化。
一、数据仓库是个啥?和数据库有什么不同?
1.1 数据库

       数据库的基本概念,大家应该都不陌生。如今但凡是个业务系统,都或多或少需要用到数据库。 即便我们不直接跟数据库打交道,它们也在背后默默滴为我们服务,比如刷个卡、取个钱,后台都是数据库在扛着。
数据库主要用于「事务处理」,存取款这种算是最典型的,特别强调每秒能干多少事儿。
没有数据仓库时,我们需要直接从业务数据库中取数据来做分析。业务数据库主要是为业务操作服务,虽然可以用于分析,但需要做很多额外的调整,主要有以下几个问题:结构复杂,数据脏乱,难以理解,缺少历史,大规模查询缓慢。

1.1.1 结构复杂
       业务数据库通常是根据业务操作的需要进行设计的,遵循3NF范式,尽可能减少数据冗余。这就造成表与表之间关系错综复杂。在分析业务状况时,储存业务数据的表,与储存想要分析的角度表,很可能不会直接关联,而是需要通过多层关联来达到,这为分析增加了很大的复杂度。
举例:想要从门店的地域分布来分析用户还款情况。基本的还款数据在订单细节表里,各种杂项信息在订单表里,门店信息在门店表里,地域信息在地域表里,这就意味着我们需要把这四张表关联起来,才能按门店地域来分析用户的还款情况。
此外,随着NoSQL数据库的进一步发展,有许多数据储存在诸如MongoDB等NoSQL数据库中,另外一些日志信息,通常也不会在数据库中有记录,而是以文本文件的形式储存。多种多样的数据储存方式,也给取数带来了困难,没法简单地用一条SQL完成数据查询。如果能把这些数据都整合到一个数据库里,构建成一张大宽表,这样就能很方便地完成数据查询,从而提高分析效率。
1.1.2 数据脏乱
       因为业务数据库会接受大量用户的输入,如果业务系统没有做好足够的数据校验,就会产生一些错误数据,比如不合法的身份证号,或者不应存在的Null值,空字符串等。
1.1.3 理解困难
       业务数据库中存在大量语义不明的操作代码,比如各种状态的代码,地理位置的代码等等,在不同业务中的同一名词可能还有不同的叫法。
这些情况都是为了方便业务操作和开发而出现的,但却给我们分析数据造成了很大负担。各种操作代码必须要查阅文档,如果操作代码较多,还需要了解储存它的表。来自不同业务数据源的同义异名的数据更是需要翻阅多份文档。
1.1.4 缺少历史
       出于节约空间的考虑,业务数据库通常不会记录状态流变历史,这就使得某些基于流变历史的分析无法进行。比如想要分析从用户申请到最终放款整个过程中,各个环节的速度和转化率,没有流变历史就很难完成。
1.1.5 大规模查询缓慢
       当业务数据量较大时,查询就会变得缓慢。尤其需要同时关联好几张大表,比如还款表关联订单表再关联用户表,这个体量就非常巨大,查询速度非常慢。大量时间都浪费在了等待查询结果上,真是令人叹息。
1.2 数据仓库
       数据仓库,通常是业务发展到一定规模后,业务分析师、CIO、决策者们,希望从大量的应用系统、 业务数据中,进行关联分析,最终整点干货出来。 比如为啥利润会下滑?为啥库存周转变慢了?向数据要答案,整点报告、图表出来给老板汇报,辅助经营决策。

可是,数据库"脑容量不足",擅长事务性工作,不擅长分析型的工作,于是就产生了数据仓库。
虽然现在 HTAP 的概念很盛行,也就是混合事务/分析处理,用一套数据库架构来同时
支持事务(OLTP)和分析(OLAP)两种需求,但真正大规模的分析和洞察,还是离不开数据仓库。
数据仓库相当于一个集成化数据管理的平台,从多个数据源抽取有价值的数据,在仓库内转换和流动,并提供给 BI 等分析工具来输出干货。

因为分析型业务需要大量的"读"操作,所以数据仓库通过“Denormalized”化的方式优化表结构,减少表间联接,牺牲空间来换取读性能。(一张表里的冗余数据增加了,但查询起来却更快了),并使用列式存储优化,来进一步提高查询速度、降低开销。
再结合面向分析场景的 Schema 设计,数据仓库就可以高效率、全方位、多维度的扛
起"联机分析"重任了。


例如上万个人去银行产生的数据,还能勉强搞定,但是当数据体量上来了,成百万上千万上亿个,甚至更多呢?你会说银行有Oracle这种强大的数据库,但是传统数据库目前来说,只能做到处理、读写、删除一些需求,更多的还是存储数据的用途。
把大体量数据聚合在一起分析,数据库很难做到。于是人们在现有的数据库基础上,对数据进行加工,也就是常说的ETL或ELT:抽取、转换、加载。

然后,数据仓库就生成了,里面有各种不同的数据,分成不同的业务包,都是为了数据分析,用于BI和报表上面。
关于数据库和数据仓库的区别,总结如下。

二、数据湖
数据库负责干事务处理相关的事,数据仓库负责干业务分析相关的事,还有新兴的 HTAP 数据库既干事务又干分析,都已经这么内卷了,还要数据湖来干啥? 
说白了,还是企业在持续发展,企业的数据也不断堆积,虽然“含金量”最高的数据都存 在数据库和数仓里,支撑着企业的运转。

但是,企业希望把生产经营中的所有相关数据,历史的、实时的,在线的、离线的,内 部的、外部的,结构化的、非结构化的,都能完整保存下来,方便“沙中淘金”。
可是时间长了,有人觉得数仓还不够,再造一个概念?
我有时候不一定用得到现在的数据,需要过往的数据,怎么办?那就把所有数据汇集在一起,无论是IoT还是CRM等,先把这些数据和文件存在一起,等用的时候再说。
数据湖里,可能有如下的这些数据结构:

数据库和数据仓库都干不了这活儿,怎么办呢? 挖个大坑,修个湖,把各种数据一滚脑灌进去囤起来,而且要持续灌,持续囤。这就是数据湖啦。


数据湖的本质,是由"数据存储架构+数据处理工具"组成的解决方案,而不是某个单一独立产品。 
1>数据存储架构,要有足够的扩展性和可靠性,要满足企业能把所有原始数据都“囤”起来,存得下、存得久。一般来讲,各大云厂商都喜欢用对象存储来做数据湖的存储底座,比如Amazon Web Services(亚马逊云科技),修建"湖底"用的"砖头",就是S3云对象存储。
2>数据处理工具,则分为两大类,第一类工具,解决的问题是如何把数据“搬到”湖里,包括定义数据源、制定数据访问策略和安全策略,并移动数据、编制数据目录等等。

如果没有这些数据管理/治理工具,元数据缺失,湖里的数据质量就没法保障,"泥石俱 下",各种数据倾泻堆积到湖里,最终好好的数据湖,慢慢就变成了数据沼泽。

因此,在一个数据湖方案里,数据移动和管理的工具非常重要。

3>第二类工具,就是要从湖里的海量数据中"淘金"。 数据并不是存进数据湖里就万事大吉,要对数据进行分析、挖掘、利用,比如要对湖里的数据进行查询,同时要把数据提供给机器学习、数据科学类的业务,便于"点石成金"。


拿Amazon Web Services来举例子,基于Amazon Athena这个服务,就可以使用标准的SQL来对S3(数据湖)中的数据进行交互式查询。再比如使用Amazon SageMaker机器学习服务,导入数据湖中的数据进行模型训练,这些都是常规操作。
总结一下,数据湖不只是个囤积数据的“大水坑”,除了用存储技术构建的湖底座以外,还包含一系列的数据入湖、数据出湖、数据管理、数据应用工具集,共同组成了数据湖解决方案。
数据湖和数据仓库区别在哪儿?

从数据含金量来比,数据仓库里的数据价值密度更高一些,数据的抽取和Schema的设计都有非常强的针对性,便于业务分析师迅速获取洞察结果,用与决策支持。
而数据湖更有一种“兜底”的感觉,甭管当下有用没有/或者暂时没想好怎么用,先保存着、沉淀着,将来想用的时候,尽管翻牌子就是了,反正都原汁原味的留存了下来。

而从产品形态看,数据仓库可以是独立的标准化产品。
数据湖则是一种架构,通常是围绕对象存储为"湖底座"的大数据管理方案组合。
比如,Amazon Web Services并没有哪个产品叫"数据湖",而是以S3为基础,结合一系列数据管理工具,帮助客户构建云上"数据湖"。

2.1 为什么要把湖和仓揉到一起?
曾经,数据仓库擅长的BI、数据洞察离业务更近、价值更大,而数据湖里的数据,更多的是为了远景画饼。
随着大数据和AI的上纲上线,原先的"画的饼"也变得炙手可热起来,为业务赋能,价值被重新定义。
而因为数仓和数据库的出发点不同、架构不同,企业在实际使用过程中,"性价比"差异很大。

数据湖起步成本较低,但随着数据体量增大,TCO成本会加速飙升,数仓则恰恰相反, 前期建设开支很大。 
总之,一个后期成本高,一个前期成本高,对于既想修湖、又想建仓的用户来说,仿佛 玩了一个金钱游戏。 
于是就想,既然都是拿数据为业务服务,数据湖和数仓作为两大"数据集散地", 能不能彼此整合一下,让数据流动起来,少点重复建设呢?

比如,让"数仓"在进行数据分析的时候,可以直接访问数据湖里的数据(Amazon Redshift Spectrum是这么干的)。再比如,让数据湖在架构设计上,就"原生"支持数仓能力(DeltaLake是这么干)。
正是这些想法和需求,推动了数仓和数据湖的打通和融合,也就是当下炙手可热的概念:Lake House(湖仓)。

2.2 湖仓一体: 融合共享阶段
提到湖仓一体,就不得不从上世纪80年代说起。当时市场还是数据仓库的天下,主要用来处理BI、仪表盘、报表等结构化数据,用于分析企业内部的业务数据。
这种状态一直持续到2010年前后,越来越多企业产生对语音、视频等数据的处理分析需求,非结构化数据、半结构化数据的增长促使企业提升了高多样性,高速度和高容量的数据分析要求,数据仓库慢慢不能满足用户的需求。随着数据仓库局限性的逐步显现,数据湖的概念也随之衍生出来,它能够存储各式各样的原始数据,解决了数据仓库的局限性。
但相比于优势来讲,湖的短板也同样明显,比如不支持事务,SQL性能差,无法支撑报表需求。虽然数据湖和数据仓都各自有各自的优势和不足,但不难发现,二者在某些层面是非常互补的。是否有一种能兼具两者优点的架构出现,于是诞生了"湖仓一体"。
湖仓一体技术的发展现状:
现阶段,数据湖与数据仓库的融合发展主要有三个技术路径,根据不同路径的技术特性,可以满足不同场景下的客户需求。
第一个是基于Hadoop体系的数据湖向数据仓库能力扩展,在数据湖中建数据仓库,通过引入数据仓库的分析功能,从数据湖直接进化到湖仓一体。
第二个是基于自身云平台进行架构构建,主要是公有云厂商基于自身云平台或相关产品,通过自研技术打通数据湖与数据仓库,实现湖仓一体功能。
第三个是以独立数据库厂商为代表的梯队,其以数据库技术为基础,自研分布式平台,从调度、计算到存储不依赖第三方平台,形成可以灵活在公有云、私有云、裸金属等场景独立部署的湖仓一体平台。这类的代表厂商如海外的Snowflake、Databricks及国内的巨杉数据库等。

2.2.1 湖仓一体1.0

       早期的湖仓一体,更多是一种处理思想,处理上直接将数据湖和数据仓库互相"打通"。数据湖从各类数据源获得原始数据,存储在廉价存储上,永久不删除。数据保持原始简单格式、机构,无数据治理,也没有数仓丰富的功能及高性能统一数据模型。
当需要支持分析场景在成熟时从数据湖到数据仓库的迁移。这种架构优点在于可充分利用先前的数据湖和数据仓库资源,利用ETL将二者“打通”,数据湖用来存储各种原始数据,分析报表交给数据仓库来完成,这算是湖仓一体的一个雏形,但湖和仓基本上还是处于各自一体的状态,架构仍然较为复杂,在满足需求的同时也持续提高了企业的运维成本。

2.2.2 湖仓一体2.0

       为了解决湖仓一体1.0的诸多问题,2.0应运而生。目前这一架构还在快速发展之中,尚无明确统一的技术框架。总的来说,可按照上图划分多层次,并在每层解决对应问题。从底层数据源,需对接多种数据源(包括结构化、半结构化及非结构化数据)。之上的数据集成需提供针对不同特征数据的集成能力(包括批量、流式)。
       处理过后的数据放入统一存储层,为面对不同结构的数据,需提供多模态存储能力,甚至为满足性能要求提供不同存储引擎。再之上是统一的元数据、安全、管控层,通过对全局数据的完整视角管理。为满足不同加工需求的统一处理层,层内提供多种加工能力。最上面是数据应用层。
       从技术上看,云原生数据仓库,为湖仓一体2.0提供有利支持,其技术上天然具备的存算分离、弹性扩展、多租户、可插拔存储、多计算引擎、分级资源管理等众多特性可满足上述要求。功能上兼具数据仓库的标准SQL、ACID能力,数据湖的大规模原始数据存储等。对上提供多种接入方式,包括标准数据库接入方式,支持高并发读写;对下支持多云、混合云及跨云部署,防止厂商绑定。其技术架构可简化为类似如下架构:

        展开说明下,其底层依旧是低成本、开放的存储,上层基于类似 Delta lake/ Iceberg/ Hudi 建设数据系统,提供数据管理特性和高效访问性能,支持多样数据分析和计算,综合了数据仓库以及数据湖的优点形成了新的架构。存算分离架构可以进行灵活扩展;减少数据搬迁,数据可靠性、一致性和实时性得到了保障;支持丰富的计算引擎和范式;此外,支持数据组织和索引优化,查询性能更优。当前湖仓一体还处于快速发展期,关键技术迭代快且成熟的产品和系统少。这里借用《DataFunCon 2021》大会上的一张图片加以说明。

湖仓一体2.0,当前仍处于相对早期的阶段,它已经不只是一个纯粹的技术概念,而是被赋予了更多与厂商产品层面相关的含义。在湖仓一体越来越火的同时,不同厂商也为它做出了各自的解读。


2.2.3 到底什么才是真正的 Lake House?
Lake House,坊间通常称之为"湖仓一体",而Amazon Web Services则叫做"智能湖仓"。 
Lake House架构最重要的一点,是实现“湖里”和“仓里”的数据/元数据能够无缝打通,并且"自由"流动。

湖里的"新鲜"数据可以流到仓里,甚至可以直接被数仓使用,而仓里的"不新鲜"数据, 也可以流到湖里,低成本长久保存,供未来的数据挖掘使用。
为了实现这个目标,Amazon Web Services推出了Redshift Spectrum,打通了数仓对数据湖的直接访问,能够高效查询S3数据湖当中的EB级数据。

    "Spectrum"是智能湖仓的核心组件,被称为"Lake House引擎",它可以在湖与仓之间架起数据流动的管道。
1>    可以将数据湖中最近几个月的“热数据”摄取到数仓中;
2>    反过来,也可以轻松将大量冷门历史数据从数仓转移至成本更低廉的数据湖内,同时这些移到湖里的数据,仍然可以被Redshift数仓查询使用; 
3>    处理数仓内的热数据与数据湖中的历史数据,生成丰富的数据集,全程无需执行任何数据移动操作; 
4>    生成的新数据集可以插入到数仓中的表内,或者直接插入由数据湖托管的外部表中。 
    做到这一步,基本上算是get到了Lake House的精髓,"湖仓一体"初见端倪。
但是,在实际业务场景下,数据的移动和访问,不仅限于数仓和数据湖之间,搜索引擎服务、机器学习服务、大数据分析服务……,都涉及到数据在本地(本系统)和数据湖之间的移动,以及数据在不同服务之间的移动。

数据积累得越多,移动起来就越困难,这就是所谓的"数据重力"。 
所以,Lake House不仅要把湖、仓打通,还要克服“数据重力”,让数据在这些服务之间按需来回移动:入湖、出湖、环湖……
把数据湖和数据仓库集成起来只是第一步,还要把湖、仓以及所有其他数据处理服务组成统一且连续的整体,这就是Amazon Web Services为何把自家的Lake House架构称为"智能湖仓",而非"湖仓一体"。

2.2 "湖仓一体"只是开局,智能湖仓才是终极。
智能湖仓并非单一产品,它描述的是一种架构。 
这套架构,以数据湖为中心,把数据湖作为中央存储库,再围绕数据湖建立专用"数据服务环",环上的服务包括了数仓、机器学习、大数据处理、日志分析,甚至RDS和NOSQL服务等等。

       大家"环湖而饲",既可以直接操纵湖内数据,也可以从湖中摄取数据,还可以向湖中回注数据,同时环湖的服务彼此之间也可以轻松交换数据。
任何热门的数据处理服务,都在湖边建好了,任何对口的数据都能召之即来、挥之则去。 依靠这种无缝集成和数据移动机制,用户就能从容地用对的工具从对的数据中,挖出干货!


       上面这张图看着就更加明白一些,中间是湖,周边集成了全套的云上数据服务,然后还有 Lake Formation、Glue、Athena以及前面重点提到的Redshift Spectrum这些工具,来实现数据湖的构建、数据的管理、安全策略以及数据的移动。

        这个六层架构,从数据源定义、数据摄取和入湖入仓,到湖仓打通与集成,再到数据出湖、数据处理和数据消费,一气呵成,各种云上数据服务无缝集成在一起。数据从各种源头“流入”到智能湖仓存储中,又按需流出,被处理、被消费。

在"智能湖仓"架构下,企业可以轻松汇集和保存海量业务数据,并随心所欲地调用各种数据服务,用于BI、可视化分析、搜索、建模、数据挖掘、特征提取、流处理等等,未来新的数据源、新的分析方法,也可以快速应对。
同时,数据湖的存储底座S3成本低廉并有近乎无限的扩展性,"湖边"大量的数据分析 和处理的服务又是无长期成本的Serverless架构,企业"入坑"智能湖仓之后,完全没有后顾之忧。
甚至可以认为,"智能湖仓"架构是比所谓"数据中台"更能落地和务实的“中台”, 如果数据中台是个饼,那智能湖仓就是把饼“烹熟烤香”的锅。

三、湖仓存储技术发展
3.1 湖仓发展历程
经过对数据湖和数据仓库的深入阐述和比较,数据湖和数据仓库作为大数据系统的两条不同演进路线,有各自特有的优势和局限性。
数据湖和数据仓库一个面向初创用户友好,一个成长性更佳。
对企业来说,数据湖和数据仓库是否必须是一个二选一的选择题?
是否能有一种方案同时兼顾数据湖的灵活性和云数据仓库的成长性,将二者有效结合起来为用户实现更低的总体拥有成本?
将数仓和数据湖融合在一起也是业界近年的趋势,多个产品和项目都做过对应的尝试。

3.1.1 数仓支持数据湖访问
2017年Redshift推出Redshift Spectrum,支持Redsift数仓用户访问S3数据湖的数据。
2018年阿里云MaxCompute推出外表能力,支持访问包括OSS/OTS/RDS数据库在内的多种外部存储。
但是无论是Redshift Spectrum还是MaxCompute的外部表,仍旧需要用户在数仓中通过创建外部表来将数据湖的开放存储路径纳入数仓的概念体系——由于一个单纯的开放式存储并不能自描述其数据本身的变化,因此为这些数据创建外部表、添加分区(本质上是为数据湖中的数据建立Schema)无法完全自动化(需要人工或者定期触发Alter table add partition 或msck)。这对于低频临时查询尚能接受,对于生产使用来说,未免有些复杂。

3.1.2 数据湖支持数仓能力
2011年,Hadoop开源体系公司Hortonworks开始了Apache Atlas和Ranger两个开源项目的开发,分别对应数据血缘追踪和数据权限安全两个数仓核心能力。
但两个项目发展并不算顺利,直到2017年才完成孵化,时至今日,在社区和工业界的部署都还远远不够活跃。核心原因数据湖与生俱来的灵活性。例如Ranger作为数据权限安全统一管理的组件,天然要求所有引擎均适配它才能保证没有安全漏洞,但对于数据湖中强调灵活的引擎,尤其是新引擎来说,会优先实现功能、场景,而不是把对接Ranger作为第一优先级的目标,使得Ranger在数据湖上的位置一直很尴尬。
2018年,Nexflix开源了内部增强版本的元数据服务系统Iceberg,提供包括MVCC(多版本并发控制)在内的增强数仓能力,但因为开源HMS已经成为事实标准,开源版本的Iceberg作为插件方式兼容并配合HMS,数仓管理能力大打折扣。
2018-2019年,Uber和Databricks相继推出了Apache Hudi和DeltaLake,推出增量文件格式用以支持Update/Insert、事务等数据仓库功能。新功能带来文件格式以及组织形式的改变,打破了数据湖原有多套引擎之间关于共用存储的简单约定。为此,Hudi为了维持兼容性,不得不发明了诸如Copy-On-Write、Merge-On-Read两种表,Snapshot Query、Incremental Query、Read Optimized Query三种查询类型,并给出了一个支持矩阵,极大提升了使用的复杂度。

    而DeltaLake则选择了保证以Spark为主要支持引擎的体验,相对牺牲对其他主流引擎的兼容性。这对其他引擎访问数据湖中的Delta数据造成了诸多的限制和使用不便。
    例如Presto要使用DeltaLake表,需要先用Spark创建manifest文件,再根据manifest创建外部表,同时还要注意manifest文件的更新问题;
而Hive要使用DeltaLake表限制更多,不仅会造成元数据层面的混乱,甚至不能写表。 
    上述在数据湖架构上建立数仓的若干尝试并不成功,这表明数仓和数据湖有本质的区别,在数据湖体系上很难建成完善的数仓。数据湖与数据仓库两者很难直接合并成一套系统,因此作者团队,开始基于融合两者的思路进行探索。
所以才提出下一代的大数据技术演进方向:湖仓一体,即打通数据仓库和数据湖两套体系,让数据和计算在湖和仓之间自由流动,从而构建一个完整的有机的大数据技术生态体系。 
构建湖仓一体需要解决三个关键问题: 
1>湖和仓的数据/元数据无缝打通,且不需要用户人工干预。
2>湖和仓有统一的开发体验,存储在不同系统的数据,可以通过一个统一的开发/管理平台操作。
4>数据湖与数据仓库的数据,系统负责自动caching/moving,系统可以根据自动的规则决定哪些数据放在数仓,哪些保留在数据湖,进而形成一体化。

3.2 数据湖技术选型
3.2.1 开源数据湖技术实现
目前市面上流行的三大开源数据湖方案分别为:Apache Iceberg、Apache Hudi、Delta Lake。

3.2.1.1 Apache Iceberg
       较为标准的数据湖公司,由Uber公司开源,目前有腾讯、阿里、网易、去哪儿网内部使用Iceberg构建数据湖。以类似于SQL的形式高性能的处理大型的开放式表。
       Netflix的数据湖原先是借助Hive来构建,但发现Hive在设计上的诸多缺陷之后,开始转为自研Iceberg,并最终演化成Apache下一个高度抽象通用的开源数据湖方案。
       Netflix用内部的一个时序数据业务的案例来说明Hive的这些问题,采用Hive时按照时间字段做partition,他们发现仅一个月会产生2688个partition和270万个数据文件。他们执行一个简单的select查询,发现仅在分区裁剪阶段就耗费数十分钟。
       他们发现Hive的元数据依赖一个外部的MySQL和HDFS文件系统,通过MySQL找到相关的parition之后,需要为每个partition去HDFS文件系统上按照分区做目录的list操作。在文件量大的情况下,这是一个非常耗时的操作。
      同时,由于元数据分属MySQL和HDFS管理,写入操作本身的原子性难以保证。即使在开启Hive ACID情况下,仍有很多细小场景无法保证原子性。另外,Hive Metastore没有文件级别的统计信息,这使得filter只能下推到partition级别,而无法下推到文件级别,对上层分析性能损耗无可避免。
      最后,Hive对底层文件系统的复杂语义依赖,使得数据湖难以构建在成本更低的S3上。
     于是,Netflix为了解决这些痛点,设计了自己的轻量级数据湖Iceberg。在设计之初,作者们将其定位为一个通用的数据湖项目,所以在实现上做了高度的抽象。
虽然目前从功能上看不如后两者丰富,但由于它牢固坚实的底层设计,一旦功能补齐,将成为一个非常有潜力的开源数据湖方案。
总体来说,Netflix设计Iceberg的核心诉求可以归纳为如下:

1>用于跟踪超大规模表的新格式,是专门为对象存储(如S3)而设计的,也支持HDFS存储。
2>在查询方面,Iceberg支持Hive、Spark、Flink、PrestoSQL,提供了建表的API,用户可以使用该API指定表明、Schema、Partition信息等,然后在Hive catalog中完成建表。

3.2.1.2 Apache Hudi
       Uber公司开源的数据湖解决方案,目前使用比较广泛;在Googgle云、微软云、IBM云阿里云、腾讯云EMR、亚马逊云EMR等大数据套件提供了对Hudi的集成支持,目前像T3出行;Hudi背后成立商业公司OneHouse,整体来说Hudi有商业公司加持发展更好。
       Uber的业务场景主要为:将线上产生的行程订单数据,同步到一个统一的数据中心,然后供上层各个城市运营同事用来做分析和处理。
      在2014年的时候,Uber的数据湖架构相对比较简单,业务日志经由Kafka同步到S3上,上层用EMR做数据分析;线上的关系型数据库以及NoSQL则会通过ETL(ETL任务也会拉去一些Kakfa同步到S3的数据)任务同步到闭源的Vertica分析型数据库,城市运营同学主要通过Vertica SQL实现数据聚合。当时也碰到数据格式混乱、系统扩展成本高(依赖收Vertica商业收费软件)、数据回填麻烦等问题。
       后续迁移到开源的Hadoop生态,解决了扩展性问题等问题,但依然碰到Databricks上述的一些问题,其中最核心的问题是无法快速Upsert存量数据。

        如上图ETL任务每隔30分钟定期地把增量更新数据同步到分析表中,全部改写已存在的全量旧数据文件,导致数据延迟和资源消耗都很高。
        此外,在数据湖的下游,还存在流式作业会增量地消费新写入的数据,数据湖的流式消费对他们来说也是必备的功能。所以,他们就希望设计一种合适的数据湖方案,在解决通用数据湖需求的前提下,还能实现快速的upsert以及流式增量消费。
        Uber团队在Hudi上同时实现了Copy On Write和Merge On Read的两种数据格式,其中Merge On Read就是为了解决他们的fast upsert而设计的。
        简单来说,就是每次把增量更新的数据都写入到一批独立的delta文件集,定期地通过compaction合并delta文件和存量的data文件。同时给上层分析引擎提供三种不同的读取视角:仅读取delta增量文件、仅读取data文件、合并读取delta和data文件。满足各种业务方对数据湖的流批数据分析需求。
最终,我们可以提炼出Uber的数据湖需求为如下图,这也正好是Hudi所侧重的核心特性:

1>提供的fast upsert/delete以及compaction等功能,管理存储在HDFS上数据,设计目标正如其名,Hadoop Upserts Deletes and Incrementals(原为Hadoop Upserts anD Incrementals)。
2>强调其主要支持Upserts、Deletes和Incrementa数据处理,支持三种数据写入方式:UPSERT、INSERT和BULK_INSERT。

3.2.1.3 Delta Lake
Spark商业公司databricks开源,跟Spark强耦合,是比较早提出数据湖概念,目前市场热度不高,很大一部分公司实时计算使用Flink,所以集成度不高。
Delta Lake是Spark计算框架和存储系统之间带有Schema信息数据的存储中间层。
它给Spark带来了三个最主要的功能:
第一,Delta Lake使得Spark能支持数据更新和删除功能;
第二,Delta Lake使得Spark能支持事务;
第三,支持数据版本管理,运行用户查询历史数据快照;

1>由于出自Databricks,Spark的所有数据写入方式,包括基于dataframe的批式、流式,以及SQL的Insert、Insert Overwrite等都是支持的(开源的SQL写暂不支持,EMR做了支持)。
2>在数据写入方面,Delta与Spark是强绑定的。
3>在查询方面,开源Delta目前支持Spark与Presto,但是Spark是不可或缺的,因为delta log 的处理需要用到 Spark。
4>用户无法高效upsert/delete历史数据,parquet文件一旦写入HDFS文件,要想改数据,就只能全量重新写一份的数据,成本很高。事实上,这种需求是广泛存在的,例如由于程序问题,导致错误地写入一些数据到文件系统,现在业务方想要把这些数据纠正过来;线上的MySQL binlog不断地导入update/delete增量更新到下游数据湖中;

3.2.2 功能对比

3.2.3 七大维度对比
从七个维度来对比评估三大项目的差异。通常在考虑数据湖方案选型时,Hive ACID也是一个强有力的候选项,因为它提供了我们需要的较为完善功能集合,所以这里把Hive ACID纳入到对比行列中。

3.2.3.1 ACID和隔离级别支持

主要解释下,对数据湖来说三种隔离分别代表的含义。
1>Serialization是说所有的reader和writer都必须串行执行;
2>Write Serialization: 是说多个writer必须严格串行,reader和writer之间则可以同时跑;
3>Snapshot Isolation: 是说如果多个writer写的数据无交集,则可以并发执行,否则只能串行。Reader和writer可以同时跑;
4>综合起来看,Snapshot Isolation隔离级别的并发性是相对比较好的。
5>Time Travel:时间旅行,意思是可以支持数据回溯、回滚,这是一个比较重要的特性。

3.2.3.2 Schema变更支持和设计

这里有两个对比项,一个是schema变更的支持情况,我的理解是hudi仅支持添加可选列和删除列这种向后兼容的DDL操作,而其他方案则没有这个限制。
另外一个是数据湖是否自定义Schema接口,以期跟计算引擎的Schema解耦。
这里Iceberg是做的比较好的,抽象了自己的Schema,不绑定任何计算引擎层面的Schema。
在Hudi 0.11.0版本中,针对Spark 3.1、Spark 3.2版本增加了schema功能的演进。如果启用 set hoodie.schema.on.read.enable=true以后,我们可以对表列和对表进行一系列的操作。列的变更(增加、删除、重命名、修改位置、修改属性),表的变更(重命名、修改属性)等。

3.2.3.3 流批接口支持
Iceberg上游组件将数据写入完成后,下游组件及时可读,可查询,可以满足实时场景。并且Iceberg同时提供了流/批读接口、流/批写接口。技术人员可以在同一个流程里,同时处理流数据和批数据,大大简化了ETL链路。

3.2.3.4 接口抽象程度和插件化

主要从计算引擎的写入和读取路径、底层存储可插拔、文件格式四个方面来做对比。
1>这里Iceberg是抽象程度做得最好的数据湖方案,四个方面都做了非常干净的解耦;
2>Delta是databricks背后主推的,必须天然绑定Spark;
3>Hudi的代码跟Delta类似,也是强绑定Spark。
存储可插拔的意思是说,是否方便迁移到其他分布式文件系统上(例如S3),这需要数据湖对文件系统API接口有最少的语义依赖,例如若数据湖的ACID强依赖文件系统rename接口原子性的话,就难以迁移到S3这样廉价存储上,目前来看只有Hive没有太考虑这方面的设计;
文件格式指的是在不依赖数据湖工具的情况下,是否能读取和分析文件数据,这就要求数据湖不额外设计自己的文件格式,统一用开源的parque、avro和orc等格式。
这里有一个好处就是,迁移的成本很低,不会被某一个数据湖方案给绑死。

3.2.3.5 查询性能优化

3.2.3.6 其它功能

       这里One line demo指的是,示例demo是否足够简单,体现了方案的易用性,Iceberg稍微复杂一点(我认为主要是Iceberg自己抽象出了Schema,所以操作前需要定义好表的schema)。做得最好的其实是delta,因为它深度跟随spark易用性的脚步。
Python支持其实是很多基于数据湖之上做机器学习的开发者会考虑的问题,可以看到Iceberg和Delta是做的很好的两个方案。
出于数据安全的考虑,Iceberg还提供了文件级别的加密解密功能,这是其他方案未曾考虑到的一个比较重要的点。

3.2.3.7 社区现状

        要说明的是,Delta和Hudi两个项目在开源社区的建设和推进方面,做的比较好。
Delta 的开源版和商业版本,提供了详细的内部设计文档,用户很是容易理解这个方案的内部设计和核心功能,同时Databricks还提供了大量对外分享的技术视频和演讲,甚至邀请了他们的企业用户来分享Delta的线上经验。
       Uber的工程师也分享了大量Hudi的技术细节和内部方案落地,研究官网的近10个PPT已经能较为轻松理解内部细节,此外国内的小伙伴们也在积极地推进社区建设,提供了官方的技术公众号和邮件列表周报。
       Iceberg相对会平静一些,社区的大部分讨论都在Github的issues和pull request上,邮件列表的讨论会少一点,不少有价值的技术文档要仔细跟踪issues和PR才能看到,这也许跟社区核心开发者的风格有关。

3.3 总结
我们把三个产品(其中Delta分为databricks的开源版和商业版)总结成如下图:

        如果用一个比喻来说明Delta、Iceberg、Hudi、Hive-ACID 四者差异的话,可以把四个项目比做建房子。由于开源的Delta是Databricks闭源Delta的一个简化版本,它主要为用户提供一个table format的技术标准,闭源版本的Delta基于这个标准实现了诸多优化,这里我们主要用闭源的Delta来做对比。

1>Delta 的房子底座相对结实,功能楼层也建得相对比较高,但这个房子其实可以说是 Databricks 的,本质上是为了更好的壮大Spark生态,在Delta上其他的计算引擎难以替换 Spark 的位置,尤其是写入路径层面;
2>Iceberg的建筑基础非常扎实,扩展到新的计算引擎或者文件系统都非常的方便,但是现在功能楼层相对低一点,目前最缺的功能就是upsert和compaction两个,Iceberg社区正在以最高优先级推动这两个功能的实现;
3>Hudi的情况要相对不一样,它的建筑基础设计不如Iceberg结实,举个例子,如果要接入Flink作为Sink的话,需要把整个房子从底向上翻一遍,把接口抽象出来,同时还要考虑不影响其他功能,当然Hudi的功能楼层还是比较完善的,提供的upsert和compaction功能直接命中广大群众的痛点。
4>Hive的房子,看起来是一栋豪宅,绝大部分功能都有,把它做为数据湖有点像靠着豪宅的一堵墙建房子,显得相对重量级一点,另外正如Netflix上述的分析,细看这个豪宅的墙面是其实是有一些问题的。

三种数据湖技术实现总结:
        三个引擎的初衷场景并不完全相同,Hudi为了incremental的upserts,Iceberg定位于高性能的分析与可靠的数据管理,Delta定位于流批一体的数据处理。
这种场景的不同也造成了三者在设计上的差别。尤其是Hudi,其设计与另外两个相比差别更为明显。因此后面是趋同还筑起各自专长优势壁垒未可知。
Delta、Hudi、Iceberg三个开源项目中,Delta和Hudi跟Spark的代码深度绑定,尤其是写入路径。这两个项目设计之初,都基本上把Spark作为他们的默认计算引擎了。
        而Apache Iceberg的方向非常坚定,宗旨就是要做一个通用化设计的Table Format。
它完美的解耦了计算引擎和底下的存储系统,便于多样化计算引擎和文件格式,很好的完成了数据湖架构中的Table Format这一层的实现,因此也更容易 成为Table Format层的开源事实标准。
        另一方面,Apache Iceberg也在朝着流批一体的数据存储层发展,manifest和snapshot的设计,有效地隔离不同transaction的变更 ,非常方便批处理和增量计算。并且,Apache Flink已经是一个流批一体的计算引擎,二者可以完美匹配,合力打造流批一体的数据湖架构。
        最后,Apache Iceberg这个项目背后的社区资源非常丰富。
        在国外,Netflix、Apple、Linkedin、Adobe等公司都有PB级别的生产数据运行在Apache Iceberg上;在国内,腾讯这样的巨头也有非常庞大的数据跑在Apache Iceberg之上,最大的业务每天有几十T的增量数据写入。
社区成员同样非常资源和多样化,拥有来自其他项目的7位ApachePMC ,1为VP。体现在代码和设计的review上,就变得非常苛刻,一个稍微大一点的PR涉及100+的comment很常见。这些都使得Apache Iceberg的设计+代码质量比较高。
        Apache Iceberg 0.10.0 版本的发布,已经拉开集成Flink和Iceberg 的序幕。
       基于以上,个人比较推荐选择Apache Iceberg,并且建议和Apark Flink搭配。

3.4 基于Iceberg技术数据湖实现
        Iceberg是Netflix在2018年开源,而后捐赠给Apache基金会的项目。Iceberg最初的功能相比Delta或Hudi少一些,但是得益于底层架构设计得非常优雅和通用,因此较早地实现了Flink的读写,在国内也获得了不少关注度,诸如腾讯和网易等大厂都有在使用Iceberg。
        Apache Iceberg官网定义:Iceberg是一个通用的表格式(数据组织格式),用于大型分析数据集(TableFormat),它可以适配Spark、Flink、Trino等引擎提供高性能的读写和元数据管理功能。
        从Iceberg的定义中不难看出,这类技术它的定位是在计算引擎之下,又在存储之上。同时,它也是一种数据存储格式,Iceberg称其为"Table Format"。这类技术可以看作介于计算引擎和数据存储格式中间的数据组织格式,通过特定的方式将数据和元数据组织起来,所以称之为数据组织格式更为合理。
        表格式可以理解为元数据及数据文件的一种组织方式。Iceberg底层数据存储可以对接HDFS、S3文件系统,并支持多种文件格式,处于计算框架(Spark、Flink、Hive)之下,数据文件之上。

Iceberg为包括Spark、Trino、PrestoDB、Flink和Hive在内的多种计算引擎提供高性能的SQL表格式。
通过该表格式,将下层的存储介质(HDFS、S3、OSS等)、文件格式(Parquet、Avro、ORC等)与上层计算引擎(Flink、Spark、Presto、Hive等)进行解耦。如图所示:

计算与存储的解耦给我们带来了更多的灵活性,在计算引擎上有了更多的选择,可以根据实际的需求选择不同的计算引擎。通过表格式屏蔽了下层的存储细节,对上层引擎呈现的都只是一张 Iceberg 表。

3.4.1 核心能力
Apache Iceberg设计初衷是为了解决Hive数仓上云的问题,经过多年迭代已经发展成为云上构建数据湖服务的表格式标准。更多介绍,请参见Apache Iceberg官网: 
https://iceberg.apache.org/?spm=a2c63.p38356.0.0.32294035DxWMbF
目前Iceberg提供以下核心能力:
1>支持基于HDFS或者对象存储构建低成本的轻量级数据湖存储服务。
2>支持Parquet、orc、avro等存储格式。
3>支持主流开源计算引擎(Spark、Flink、Hive、PrestoDB等)入湖和分析场景的完善对接。
4>支持数据的ACID操作,支持行级数据变更能力。
5>支持历史版本回溯,支持查询特定版本的数据。
6>支持高效的数据过滤。
7>支持Schema变更(Schema Evolution),包括add、drop、update、rename等操作。
8>支持隐式分区(Hidden Partitioning)。
9>支持分区布局变更(Partition layout Evolution)。
10>支持python和java。
有些功能可能会受引擎制约导致不支持,比如Hive不支持数据的Update。
为了便于理解数仓和Iceberg数据湖在系统架构、业务价值和成本方面的差异,选择了业界流行的Clickhouse实时数仓、Hive离线数仓和Iceberg数据湖三种具体的技术架构,进行了对比,信息如下。

与开源Iceberg对比
从基础功能、数据变更和计算引擎等方面,对比了阿里云Iceberg与开源Iceberg,信息如下。

适用场景
Iceberg作为通用数据湖解决方案中最核心的组件之一,主要适用于以下场景。

3.4.2 Iceberg特性
3.4.2.1 数据存储、计算引擎插件化
    随着Flink等技术的不断发展,流批一体生态不断完善,但在流批一体数据存储方面一直是个空白,直到Iceberg等数据湖技术的出现,这片空白被慢慢填补。
    Iceberg提供了基于流式的增量计算模型和基于批处理的全量表计算模型。批处理和流任务可以使用相同的存储模型,数据不再孤立;
Iceberg屏蔽了底层数据存储格式的差异,提供对于Parquet,ORC和Avro格式的支持。
Iceberg起到了中间桥梁的能力,将上层引擎的能力传导到下层的存储格式。
    Iceberg在设计之初的目标就是提供一个开放通用的表格式(Table Format)实现方案。
因此,它不和特定的数据存储、计算引擎绑定。目前大数据领域的常见数据存储有HDFS、S3、阿里/华为云对象存储,计算引擎有Flink、Spark、Hive、Trino都可以接入Iceberg。
在生产环境中,技术人员可以根据公司的实际情况,选择不同的组件搭配使用。甚至可以不通过计算引擎,直接读取存在文件系统上的数据。
注意:Trino就是原来的PrestoSQL,2020年12月27日,PrestoSQL项目更名为Trino,Presto分成两大分支:PrestoDB、PrestorSQL。

3.4.2.2 Iceberg数据存储格式
为了便于理解Iceberg的几个重要的特性,先简单介绍下Iceberg的文件的组织形式。
Iceberg文件组织分为四层,分别为Metadata、Snapshot、Manifest、DataFile。

3.4.2.2.1 Metadata文件
该文件记录了最新的快照信息和历史的快照记录。并且记录了最新的Schema信息。

3.4.2.2.2 Snapshot文件(表快照)
快照代表一张表在某个时刻的状态。每个快照里面会列出表在某个时刻的所有data files列表。data files是存储在不同的manifest files里面,manifest files是存储在一个Manifest list文件里面,而一个Manifest list文件代表一个快照。
由于Iceberg基于MVCC(多版本并发控制)的设计理念,每次Commit都会生成一个Snapshot,该Snapshot是当时表的全局快照,即选定某个快照读取时,读到的是全量数据。
Snapshot文件记录了历史的 Manifest文件和本次Commit新增的Manifest,当我们增量读取时,只需要读取指定快照的新增的 Manifest 就可以实现读取新增的数据。

3.4.2.2.3 Manifest list(清单列表)
manifest list是一个元数据文件,它列出构建表快照(Snapshot)的清单(Manifest file)。
manifest list中存储的是Manifest file文件的列表,每个Manifest file占据一行。每行中存储了Manifest file的路径、其存储的数据文件(data files)的分区范围,增加了几个数据文件、删除了几个数据文件等信息,这些信息可以用来在查询时提供过滤,加快速度。

3.4.2.2.4 Manifest file(清单文件)
该文件记录了本次事务中写入的文件和分区的对应关系,并且记录了文件中字段的一些统计信息(如最大值、最小值)以便于快速查找。
Manifest file也是一个元数据文件,它列出组成快照(snapshot)的数据文件(data files)的列表信息。每行都是每个数据文件的详细描述,包括数据文件的状态、文件路径、分区信息、列级别的统计信息(比如每列的最大最小值、空值数等)、文件的大小以及文件里面数据行数等信息。其中列级别的统计信息可以在扫描表数据时过滤掉不必要的文件。
Manifest file是以avro格式进行存储的,以".avro"后缀结尾,例如:
8138fce4-40f7-41d7-82a5-922274d2abba-m0.avro。
3.4.2.2.5 DataFile(数据文件)
DataFile是实际写入的数据文件,存储支持不同的文件格式,目前支持parquet、ORC、Avro格式文件。
数据文件是Apache Iceberg表真实存储数据的文件,一般是在表的数据存储目录的data目录下。如果我们的文件格式选择的是parquet,那么文件是以".parquet"结尾,例如:
00000-0-root_20211212192602_8036d31b-9598-4e30-8e67-ce6c39f034da-job_1639237002345_0025-00001.parquet 就是一个数据文件。
Iceberg每次更新会产生多个数据文件(data files)。

3.4.2.3 事务支持(ACID)
        Iceberg的ACID能力可以简化整个流水线的设计,降低整个流水线的延迟,降低数据修正的成本。传统Hive/Spark在修正数据时需要将数据读取出来,修改后再写入,有极大的修正成本。Iceberg 所具有的修改、删除能力能够有效地降低开销,提升效率。
        Iceberg提供了锁的机制来提供ACID能力,上游数据写入即可见,从而数据可以更快的被下游组件消费。同时Iceberg保证了线性一致性(Serializable isolation),确保表的修改操作是原子性的,读操作永远不会读到部分或是没有commit的数据。
        Iceberg提供了乐观锁的机制降低锁的影响,并且使用冲突回退和重试机制来解决并发写所造成的冲突问题。
        Iceberg提供了upsert、merge into能力,可以极大地缩小数据入库延迟;
        Iceberg支持in-place table evolution,用户可以像SQL那样修改表的schema,或者修改分区方式。Iceberg无需用户重写表数据或者是迁移到新表上。
        基于ACID的语义支持,通过Snapshot进行读写分离,提供了Serializable isolation,且所有的操作都可以保证原子性。
        相比Hive而言,Iceberg提供的事务性可以隔离写入任务队分析任务的不利影响,且写入失败不会出现脏数据。

更加吸引人的是Iceberg和Flink的结合,通过Flink的Checkpoint机制和Iceberg的事务性,可以做到端到端的Exactly once语义。

3.4.2.4 Schema约束与Schema evolution


3.4.2.4.1 Schema约束
       提起一张表(table format),最先强调的是表是具有Schema的,Iceberg表是有 Schema强制约束的。与Hive表不同,写入Iceberg表的数据必须经过Schema的校验,这样就解决了Hive表文件内容和Schema不匹配导致的下游消费异常的问题。


3.4.2.4.2 Schema evolution
       数据随着时间和业务量的增长会需要有一些格式上的变化,例如添加新的纬度,更细的分区粒度等。传统的Hive表如果想要处理这些变化可能需要创建一个新的表,将旧的数据读出来再写到新的表里。如果表的分区粒度也需要发生变化,例如,分区从天变成小时,那么还需要上层更改相关的查询语句,甚至还有引起正确性的问题。
       所以表结构更新Schema Evolution是新一代数据湖的一个重要特性,使用新一代数据湖的Schema Evolution特性很容易对表的结构进行一些微调,例如增减某些列,从而满足用户数据变化的需求。
       作为构建新一代数据湖的主流中间件,Apache Iceberg支持Full Schema Evolution的功能,包括添加列,删除列,更新列,更新分区列等操作。用户可以任意对表的结构进行in-place table evolution的更新,包括对普通列以及嵌套类型的列进行结构更新,还支持对分区的列进行更新。Iceberg的表结构更新是内在的元信息更新,不用付出重写表数据或者是数据迁移到新表上的代价。
       Iceberg在Metadata文件中记录最新的Scehma结构,并通过id与实际的数据文件中的Schema进行映射。因此对Iceberg表可以进行更加灵活的Schema变更操作,可以像MySQL那样修改表的Schema,对Iceberg表进行增加列、删除列、更新列等操作,并且这些操作不会有任何的副作用。而在使用Hive表进行Schema变更时,在某些情况下则需要将历史的分区数据全部重写才可以完成。
Iceberg支持Schema变更操作:
Add: 添加新的到表,支持添加到嵌套类型列。
Drop: 删除表的某个列,支持删除嵌套类型。
Rename: 重命名列,支持嵌套类型。
Update: 改变列的类型,例如Integer更新成Long,支持更新struct、map、list复杂类型。
Reorder: 改变列的排序,包括嵌套结构中字段的排序顺序。
注意:Iceberg Schema的更改只改变metadata,不会涉及到重写数据文件。
Map结构类型不支持Add和Drop字段。
Iceberg实现以上功能的原因是,使用唯一的id来追踪表中的每一列,当添加一个列时,会分配新的ID,因此列对应的数据不会被错误使用。


3.4.2.5 Hidden Partition与Partition evolution


3.4.2.5.1 Hidden Partition(隐式分区)
       与Hive表类似,Iceberg也可以进行分区来获取更快的查询。但与Hive不同的地方时Iceberg支持隐式分区。
在Hive中,分区需要显示指定为表中的一个字段,并且要求在写入和读取时需要明确的指定写入和读取的分区,如下示例中,以event_date作为分区分别展示读写Hive表和Iceberg表。

       在上述例子中,Hive表并不知道event_date和event_time的对应关系,需要用户来跟踪。
       而在Iceberg中将分区进行隐藏,由Iceberg来跟踪分区与列的对应关系。在建表时用户可以指定date(event_time) 作为分区, Iceberg 会保证正确的数据总是写入正确的分区,而且在查询时不需要手动指定分区列,Iceberg 会自动根据查询条件来进行分区裁剪。

3.4.2.5.2 Partition evolution(分区演化)
        和Spark与Hive不同,Iceberg采用的分区方式是隐式分区,这种隐式分区的方式使得分区更加灵活,可以通过以某些列作为输入,然后指定一个变换函数结合起来作为一个分区格式。例如,假设表的设计里面有一列是event_time,那么可能的分区方法有:
date(event_time):根据日期分区
mouth(event_time):根据月分区
year(event_time):根据年分区
day(event_time):根据天分区
bucket(event_time, 10):分成10个桶
identity(event_time):根据event_time分区
truncate(event_time, 5):根据event_time前5位的宽度分区
当然如果你觉得这些都不够用, 那么你还可以自己写一个Transform 接口的实现来指定分区策略。
        在隐式分区技术的基础上,Iceberg实现了Partition Evolution,这个功能可以让上层的查询语句不需要做任何的更新,仍然可以无缝的使用分区过滤功能。这非常关键,例如原先你的数据是按照日期进行分区,随着数据不断增长原先分区里的数据越来越多,分区过滤后数据还是很多,现在想换下分区策略,改成按小时分区。
原先的一个查询SQL:
SELECT level, count(1) as count
FROM logs
WHERE event_time
BETWEEN '2022-10-01 10:00:00' AND '2022-10-01 14:00:00'
AND event_date = '2022-10-01'
如果是不使用Iceberg,那么正常的变更分区方式可能需要更新表,重新添加一个虚拟列hour作为分区列。同时需要添加一个条件语句是and hour = '10', 如果上层查询语句没有更新,那么分区过滤也无效。
        Iceberg分区可以在现有表中更新,因为Iceberg查询流程并不和分区信息直接关联。
当我们改变一个表的分区策略时,对应修改分区之前的数据不会改变,依然会采用老的分区策略,新的数据会采用新的分区策略写入。
        也就是说同一个表会有两种分区策略,旧数据采用旧分区策略,新数据采用新新分区策略,在元数据里两个分区策略相互独立,不重合。
        当我们将分区由按月分区调整为按天分区后,在写SQL进行数据查询时,如果存在跨分区策略的情况,则会解析成两个不同执行计划,如Iceberg官网提供图所示:


    Schema Evolution是新一代数据湖必备的技能,不管是Iceberg, Delta Lake, Hudi都宣称自己有Schema Evolution的功能,但只有Iceberg真正做到full schema evolution。
    Iceberg的亮点在于它把Schema的逻辑独立抽象出来,使得Schema和Partition与上层引擎和底层的文件格式解耦做到更强大的功能。

3.4.2.6 行级更新
    Iceberg表的一大亮点是提供了OLAP场景下列式存储数据集的更新和删除的能力。
由于Iceberg表支持更新和删除,因此你可以将Changelog(如Binlog数据)导入到Iceberg表中进行分析,同时也可以操作Iceberg表以更新或删除某一行或者一批数据。
    而在Hive中,由于Hive不支持更新,因此每次只能全量写入,浪费了计算资源,且存在较多的冗余数据。
    Iceberg表的更新是采用MOR(Merge on Read)模式来实现的,任何的更新和删除操作并不会对原数据文件进行操作,而是采用追加的形式写入到另一个文件中(Delete File),在读取时进行Merge操作以获取正确的结果。

3.4.2.7 Snapshots与Time travel

3.4.2.7.1 快照读写分离,数据组织管理更为完善
Iceberg表每一次Write都会产生一个新的Snapshot,同时也会产生一个新的Version版本。所以对于流式写入,会产生大量的Snapshot。
元数据组织主要用来记录表结构、分区信息、数据存放位置等信息。
1>    跟踪旧快照,并对其生命周期进行管理。
2>    表的元数据是不可修改的,并且始终向前迭代。
3>    数据读取仅使用当前已经生成的快照,写操作会生成新的快照隔离,并在完成数据写入后原子性提交,以及对于文件列表的所有修改都是原子操作。
4>    每次写操作都会产生一个新的快照(snapshot),快照始终是往后线性递增,确保了线性一致性。而读操作只会读取已经存在了的快照,对于正在生成的快照,读操作是不可见的。
5>    每一个快照拥有表在那一时刻所有的数据和元数据,因此提供了用户回溯(time travel)表数据的能力。

3.4.2.7.2 Time travel
Time travel(时间旅行)是一个比较重要的特性
Iceberg架构决定所有历史信息都会被记录下来,所以我们可以通过API 或者SQL得到过去某个时间点的Snapshot id,然后做历史时刻的查询。
这里历史数据不仅仅包括数据本身信息,还包括历史的DDL,Schema的变更信息都可以被保存下来,这也为上层服务定位问题提供便捷。
由于Iceberg多版本的设计,允许用户在多个快照之间进行切换。
通过Time travel用户可以实现如下操作:
1>    历史数据查询:指定某一个Snapshot来查询某一时刻的快照数据。
2>    增量消费:指定起始和终止的Snapshot,查询某时间内新增的数据。
3>    快照回滚:当出现脏数据时可以回滚到某一个快照。
4>    回溯:指定某一个快照开始消费以进行数据重放。
此外Snapshots记录了本次Commit的变更,可以通过查询、监控Snapshots的信息以监控数据的并更情况,及时发现数据的质量问题。

3.4.2.8 Iceberg数据类型

3.4.3 数据湖底层存储选型
3.4.3.1数据仓库要点
1>存储和计算绑定;
2>专有数据格式;
3>数据结构类型较为单一;
4>对可靠性、一致性、数据事务要求较高;
5>细粒度的数据权限控制;
6>由于存储和计算绑定,容易优化,性能较高;
7>扩展性能较一般;
8>预先建模,写入型schema,按预设的schema读写;
9>主要存储计算处理后的数据;
10>主要用于Report、BI(有实时性要求);

3.4.3.2 数据湖要点
1>存储和计算解耦分离,支持多种存储,支持多种计算引擎;
2>支持开放通用的数据格式;
3>支持多种数据格式,支持存储任意类型的数据,包括结构化、半结构化和非结构化数据;
4>优化和性能,比存储计算一体化的方案要差些;
5>低存储成本,高扩展性;
6>无须提前建模,读取型 schema,可以在读取时才确定 schema,灵活度高;
7>主要存储原始数据,也存储计算处理后的数据;
8>数据湖需要具备完善的数据生命周期管理能力。不光需要存储原始数据,还需要能够保存各类分析处理的中间结果,并完整的记录数据的分析处理过程,能帮助用户完整详细追溯任意一条数据的产生过程;
9>数据湖需要具备完善的数据管理能力(完善的元数据),可以管理各类数据相关的要素,包括数据源、数据格式、连接信息、数据Schema、权限管理等;
10>数据湖需要具备多样化的分析能力,包括但不限于批处理、流式计算、交互式分析以及机器学习,也可以用于Report、BI但性能差些;还需要提供一定的任务调度和管理能力;
11>数据湖需要具备完善的数据获取和数据发布能力。数据湖需要能支撑各种各样的数据源,并能从相关的数据源中获取全量/增量数据;然后规范存储。数据湖能将数据分析处理的结果推送到合适的存储引擎中,满足不同的应用访问需求;

3.4.3.3 Iceberg对象存储/HDFS文件存储
3.4.3.3.1 对象存储支撑数据湖
数据湖目前社区里面已经有的Iceberg Catalog实现可分为两个部分,一是数据IO部分,二是元数据管理部分。

        绝大多数Catalog使用Aapche HDFS作为数据文件的存储,HDFS是当前被广泛使用的开源存储组件。它拥有一个文件系统抽象,从而让开发者更加便利的使用其作为存储引擎。
        但是在近1-2年以来,随着混合云/多云和边缘端的高速发展,其相较而言更加简单的抽象也能够被大数据生态所使用,进而出现取代 HDFS 的趋势。
        如上图所示,缺少面向私有对象存储的Catalog实现,S3A理论上可以接对象存储,但它用的是文件系统语义,不是天然的对象存储语义,模拟这些文件操作会有额外的开销,而我们想实现的是把数据和元数据管理全部都交给一个对象存储,而不是分离的设计。

3.4.3.3.2 对象存储和HDFS的比较
这里存在一个问题,在有HDFS的情况下,为什么还要用对象存储?
我们可从不同角度将对象存储和HDFS进行对比

总结如下:
1>对象存储在集群扩展性,小文件存储方面友好,在多站点部署和低存储开销上更加有优势;
2>HDFS在Append(追加上传)和原子性Rename上的设计优势,正是Iceberg需要的,这两方面对于对象存储而言仍旧存在一些挑战;
以下对两种存储各自的优势进行简单阐述

3.4.3.3.2.1 集群扩展性

        HDFS架构是用单个Name Node保存所有元数据,提供元数据管理服务。Name Node实际上成为了HDFS系统中的单点服务,决定了它单节点的能力有限,在元数据方面原生不支持横向扩展能力。
        在逐步增长的数据规模下,这样的架构会慢慢的体现出劣势,往往体现在需要使用越来越高的配置运行Name Node,需要对Name Node设计横向扩展及高可用方案。
        在对象存储中,集群的横向扩展往往比较容易。对象存储的元数据通常使用一致性哈希方式对元数据进行分区处理,把元数据分割成各个块,把块交给不同Node上面的服务来进行管理。
        当数据规模增长到分区的限制时,对象存储可以rehash(重新设定分区),把块切得更细,交给更多的Node来管理元数据,以支持更高规模的扩展能力。

3.4.3.3.2.2 小文件友好

    如今在大数据应用中,小文件越来越常见,并逐渐成为一个痛点。
    如前所述,HDFS的元数据受限于Name Node的架构。当出现海量小文件时(例如单节点100亿-150亿量级4KB文件),因为其元数据的占比较高,HDFS的Name Node空间消耗极大,导致Data Node在尚空的情况下,Name Node已经无法有效的提供服务。
        社区使用了多种手段优化小文件存储,但其基本原理都是将小文件合并成大文件,这或多或少对性能和交互便利性产生了一定的影响。例如HDFS提供了Archive的方法来合并小文件,减少对Name Node的压力,但这需要额外增加复杂度,因为不是原生的。
同样,小文件的TPS也是受限于Name Node的处理能力,因为它只有单个Name Node。
        对象存储使用了分布式的元数据管理设计,本质上元数据和数据都是用一致的底层存储方式,可以无缝的scale out到整个底层硬件层上,解决了元数据容量扩充的挑战,对于大量小文件有着容量和性能上的优势。
        近年来,对象存储在快速发展中通常利用多种介质对元数据进行加速或缓存,使得其可以利用存储领域的新技术,例如全面引入NVMe-oF,对元数据查询进行优化。
        所以在对象存储中,元数据不再受限于单个节点的物理资源,对于小文件这种元数据与数据接近的数据湖场景,对象存储更能够平衡元数据和数据的资源配比,有效的利用整个系统的物理资源对小文件进行索引,使得单一的节点也能够容纳海量小文件,而不受限于某些特定节点的资源限制。

3.4.3.3.2.3 多站点部署

         对于存储的数据,如果需要异地备份,或者多机房备份,就需要进行多站点部署。
HDFS本身并不原生支持这种跨服务的部署。虽然目前有一些商业版本给HDFS增加了多站点负责数据的能力,但由于它的两个系统可能是独立的,因此并不能支撑真正的全局命名空间下多活的能力。
        对象存储的多站点部署能力适用于两地三中心多活的架构,绝大部分对象存储则天然支持多站点部署。通常用户只要配置数据的复制规则,对象存储就会建立起互联的通道,将增量或存量数据进行同步。
        对于配置了规则的数据,你可以在其中任何一个站点进行访问,由于跨站点的数据具备最终一致性,在有限可预期的时间内,用户会获取到最新的数据。
综上所述,一个支持多站点数据访问的单一存储抽象,是现阶段HDFS所不能提供的。

3.4.3.3.2.4 低存储开销

        任何分布式存储的在设计上都需要一些额外的副本数据来抵御硬件故障产生的数据丢失风险。
HDFS默认使用3副本存储数据,数据存储了3份,存储开销是三倍。对于其中任意2份数据,如果因为软硬件故障发生了损坏,可以使用剩余的1份,保障了数据的准确性。但是,对于一次写入-多次读取的场景,其存储开销较大。对于存储有限的用户,往往希望在保障数据准确性的前提下,降低多副本带来的存储空间浪费。
        因此,EC(纠删码)成为了存储领域的一种解决方案。
        新的HDFS版本上已经支持EC的能力。经过研究,它是基于文件做EC,所以它对小文件有天然的劣势。
        由于是对文件整体进行EC编码,所以在小文件时开销会变大,极端情况下当文件小于最小分片大小时,EC算法可能无法拆分出足够的数据块,导致EC过后反而出现空间变大的情况。与此同时,HDFS上经过EC的文件会带来很多限制,例如不再支持Append追加上传、hflush、hsync等操作,这会极大地影响EC能够使用的场景。
        对象存储原生支持EC,数据使用了10+2的EC编码,它把数据切分成10份数据块,根据这10份数据块计算出2份纠删数据块,一共12个块,接着分布到四个节点上,存储开销是1.2倍。它同样可以容忍同时出现两个块故障,这12份数据块中,用任意10份数据块就可以算出完整数据。在这种模式下,数据密度被提升,有限的存储可以发挥出更高的价值。
        而在对象存储中,对于小文件的话,它内部会把小文件合并成一个大的块(Chunk)进行EC,这样确保数据开销方面始终是恒定的,在小文件上保持同样的低存储开销。

3.4.3.4 存储与计算

3.4.3.4.1 背景
       首先谈谈Hadoop的背景,说到Hadoop起源,就离不开Google的三辆马车:Google File System、MapReduce和BigTable,分别说了分布式文件系统、分布式计算MR和分布式结构化存储系统。
       Hadoop从2005年提出,采用的是存储计算耦合的架构,Hadoop将计算MapReduce、Spark和存储(HDFS)耦合部署在同一个机器上。当时不能采取存储计算分离的原因是:计算存储放一块可以避免很大的网络IO开销,网络带宽是一个最明显的瓶颈 。
       而2009年~至今,这十年的时间,网络性能从当时的百兆网卡100Mb增长到了万兆网卡10Gb,网络带宽提升了100倍,而同期的磁盘HDD整体的性能没有发生太大变化,各种高效的压缩算法和面向列的存储格式也进一步减少了IO数据量,大数据的瓶颈从IO变成了CPU。

3.4.3.4.2 存算一体
        作为大数据平台普遍的技术栈基础,Hadoop 1.0使用通用服务器和普通硬盘搭建了大规模数据存储和计算集群。在设计之初,由于单机吞吐量和集群网络带宽限制,Hadoop集群部署的存储和计算在同一服务器。
        将计算的代码移动到数据所在的地方,而不是将数据传输到计算节点,这种方式可以产生更少的数据迁移,降低机器间、机柜间的网络带宽消耗,有效解决了分散在各个弱连接的存储节点间的海量数据访问的困难。
        在传统的Hadoop集群系统中,计算和存储资源是紧密耦合的。当存储空间或计算资源不足时,只能同时对两者进行扩容。而在真实的业务场景里,不同时段对存储和计算的需求不是相当的,对它们的需求也是弹性变化的,绑定两者进行拓展的收缩,会造成资源的浪费,不满足云计算中的elastic性质。
        成本:由于存算一体,计算资源和存储资源是按某一比例强绑定,系统扩容必须按节点数目增加,导致内存或磁盘的浪费。另外由于使用3副本的数据存储模式,在大集群(100+节点、PB级别)下将造成高昂的存储成本。
       早期的大数据集群的建设,都是采用存算一体化的形式进行的,购买几台即包含计算资源又带一定存储的机型来搭建整个大数据集群,如下图:

       存算一体化的集群中每个节点都具备相同的硬件配置,早期内部典型的配置基本上是:48核,256GB内存,12块8T SATA盘,整体提供约48个CU(1CU包含1核,4GB内存)和96TB的存储。
       考虑到上述问题,不少企业开始思考这种一体化架构以及数据本地化的必要性。2012年前后,Facebook、AWS等厂商基于GFS论文中的EC算法,提出了存储和计算分离的架构原型。


3.4.3.4.3 存算分离
       在Hadoop大数据集群中,当存储空间或计算资源不足时,只能同时对两者进行扩容,会导致额外成本的增加。
       假设用户对存储资源的需求远大于对计算资源的需求,那么同时扩容计算和存储后,新扩容的计算资源就被浪费了,反之存储资源被浪费。独立扩展计算或存储的架构设计,被认为是更加灵活的扩容方式。
       因此,业内新扩容方式“存算分离”架构的优势逐渐明显,“存算分离”成了大数据架构发展的必然趋势,成了解决行业用户在使用Hadoop时,面临计算资源浪费、存储性能低、管理成本过高等痛点的利器。
       最初在Hadoop1.0时代,计算和存储是高度融合的,仅能处理单一的MapReduce分析业务;如今已经到了Hadoop3.0时代,计算存储走向分离,通过Hadoop架构策略,优化了冷热数据的存储。打造了更适合企业级市场,资源云化和灵活扩展,能够让用户享受更专业的存储,更佳的可靠性和利用率。

存算分离的需求出现
        首先从,企业的需求看,从2006年发展到2016年左右,这十年我们看到了一些新的变化, 第一企业数据增长很快,但是算力的需求其实长得没那么快。 这些任务靠人开发,不会发生一天一倍的去涨的情况,但是产生的数据的速度是是非常快的,有可能是指数型的;而且有些数据产生出来,也不一定马上知道怎么用,但未来会用,所以企业都会先把数据尽可能全量的去存起来,再去挖掘它的价值。
        在这个背景下,存算耦合的硬件的拓扑的架构就给扩容带来了一个影响,当存储不够,就要去加机器,但是不能只加机器,不能只有硬盘,因为在存算耦合的架构上,数据的节点还需要负责计算,所以CPU和内存也不能太差,因此配置的机器都是计算与存储配置非常平衡的机器,在提供足够存储容量的同时,也提供了等量的算力。
        但实际场景中算力的需求没涨。这样扩出来的算力对企业来说造成了更大的浪费,整个集群在存储和I/O上的资源利用率可能是非常不平衡的,当集群越大,这种不平衡就越严重。另外买机器也挺难的,购买的机器必须是计算与存储平衡的。
       哲学里面有句话叫"新事物总有取代旧事物的性质的趋势",随着社会的发展(IO优化、网络性能的提高),和一些新的需求的提出(资源弹性、可伸缩),和旧事物的种种弊端(资源浪费),出现了存储和计算分离的架构,这是一种架构思想,存储计算分离架构可以让大数据集群充分利用资源,可弹性拓展,更灵活,更符合云计算的特性。

存算分离这种架构有没有什么短板呢?
       答案是肯定的。在现实中,云计算存储基础设施(S3等对象存储和分布式文件系统)与计算之间有着巨大的带宽差距和高延迟,对Ad hoc类查询的执行时间和最终用户的体验有着直接的影响。
通过新增缓存层进行优化…...篇幅有限,待日后再详细描述。

3.4.3.4.3.1 基于Hadoop实现存算分离
在云上独立部署HDFS
       从2013、2014年,行业内开始看到一些存算分离架构的尝试。最初的方案比较简单,就是独立部署HDFS,不再和负责计算Worker去混合部署。这个方案在Hadoop生态里,没有引入任何的新组件。
       从下面的示意图可以看到,DataNode节点上不再部署Node Manager,意味着不再把计算任务发送到DataNode节点上。存储成为一个独立集群,计算需要用到的数据都会通过网络来传输,端到端的万兆网卡去支持,网络传输线没有在下图标出。

        在这个改变里,尽管HDFS最巧妙的数据本地性这个设计被舍弃了,但由于网络通讯速度的提高,给集群的配置带来更大的便利。Juicedata创始人Davies,2013年在Facebook工作期间,团队就做了这样的实验,发现这样的一个存算分离的改造,对整个平台性能的影响是仅仅是几个百分点,但是给集群的配置管理带来了一个还很大的便利,可以独立的部署和管理计算节点了。
       但是这个尝试没有得到进一步发展,是什么原因呢?最大的一个原因,当在机房做这样的改造是可行的,但当我们去使用云上资源的时候,这个方案的弊端就显露了。
       第一个原因,源自HDFS的多副本机制在云上会增加企业的成本。过去,企业在机房使用裸硬盘去搭建一套HDFS,为了解决裸硬损坏的风险,HDFS设计了多副本的机制,来保证数据安全性;同时多副本还承载着保证数据可用性的作用。除了磁盘损坏,当某一个 DataNode 的节点临时宕机了,这个节点上的数据访问不到了?
       多副本机制在可靠性和可用性上都发挥作用。当数据被迁移到云上时,云提供给用户的是经过多副本机制存储的云盘,不再是裸硬盘了,企业用这块云盘去搭一个HDFS,又要做3副本,企业数据在云上要存9副本,成本立马飙升了好几倍。
       后来,云也会提供一些有裸硬盘的机型,但是这类机型往往都非常少,比如说云上有100款虚拟机,云盘可以任意配置,但是有裸盘的机型只有5~10款,选择余地比较少,这些型号不一定能匹配企业的集群需要。
       第二个原因,这个方案不能让企业得到云上的独特价值,比如开箱即用,弹性伸缩,以及按量付费这些云上最大的优势。在云上部署HDFS,需要自己创建机器,手动部署和维护,自己监控和运维,而且还不能方便地扩缩容。这种情况下,HDFS上云实现存算分离,仍然有其痛点。
       第三个原因,HDFS本身的局限。首先是,NameNode只能垂直扩展,并不能分布式扩展说扩出更多的 NameNode节点,限制了HDFS单集群去管理的文件数量。
       根据实际运维经验,一般在3亿文件以内,运维HDFS还是比较轻松的,3 亿文件之后运维的复杂度就会明显提升,峰值可能就在5亿文件左右,就达到单机群的天花板了。文件量更多,需要引入HDFS的Federation联邦的机制,但是它就增加了很多的运维和管理的成本。

非云基于原生Hadoop实现存算分离
       将原生Hadoop集群从当前计算、存储一体化状态,拆分成计算和存储两个独立集群,实现“存算分离”形式,充分吸纳计算、存储两个产业的最终成果,加速释放数据价值。
       独立部署HDFS,不再和负责计算worker混合部署。这个方案在Hadoop生态里,
没有引入任何的新组件。DataNode节点上不再部署Node Manager,意味着不再把计算任务发送到 DataNode节点上,存储成为一个独立集群。
优势:
1>原生支持Append(追加上传)。
2>原生支持原子性Rename,这两个优势正是Iceberg需要的。
3>存储集群与计算集群独立部署,解耦计算和存储负载,系统负载均衡调度更加灵活。
4>更高效的存储。无论是计算资源先达到瓶颈,还是存储空间先达到瓶颈,都能够进行独立、按需扩容,持续保障存算资源的高效使用,杜绝存算一体扩容时带来的资源浪费。
5>更安全的隔离。存、算各司其职,形成逻辑隔离。计算节点不再承担数据存储,计算节点动态增减甚至是故障宕机,无需大量数据的迁移,丝毫不影响数据存储的可靠性与完整性。
劣势:
1>存算分离架构带来的性能问题和数据本地性减弱问题。通过架设缓存层加速访问速度。
2>HDFS元数据性能问题。原生单个NameNode保存所有元数据,没有横向扩展能力。
当集群的容量变得越来越大时,Name Node的负担也会越来越重。HDFS的扩容意味着需要相应的提升Name Node的配置,并且还要设计对应的横向扩展和高可用方案。
3>文件量更多,需要引入HDFS的Federation联邦的机制,但是会增加很多的运维和管理的成本。


3.4.3.4.3.2 基于云原生对象存储实现存算分离
公有云+对象存储
        随着云计算技术的成熟,企业存储又多了一个选项,对象存储。
        不同的云厂商有不同的英文缩写名,例如阿里云的对象存储服务叫做OSS,华为云OBS,腾讯云COS,七牛Kodo;对象存储适用于大规模存储非结构化数据的数据存储架构,其设计的初衷是想满足非常简单的上传下载数据,企业存储系统拥有超级强大的弹性伸缩的能力,还能保证低成本的存储。
        最早从AWS开始,后来所有的云厂商其实都在往这个方向发展,开始推动用对象存储去替代HDFS。这些方案首先带来了两个HDFS无法实现的最明显的好处:
        第一,对象存储是服务化的,开箱即用,不用做任何的部署监控运维这些工作,特别省事儿。
        第二,弹性伸缩,企业可以按量付费,不用考虑任何的容量规划,开一个对象存储的Bucket,有多少数据写多少数据,不用担心写满。
        这些方案相比在云上独立部署HDFS, 运维方面是有了很大的简化。但当对象存储被用来去支持复杂的Hadoop这样的数据系统,就会发现如下的一些问题。
1>文件Listing的性能比较弱。Listing是文件系统中最基础的一个操作。
我们在文件系统中List目录,包括HDFS里面List目录,都是非常轻量快的操作。它的性能是源于在文件系统中,数据是一个树形结构。
       对象存储没有树形结构的,它的整个存储结构是扁平的。当用户需要存储成千上万,甚至数亿个对象,对象存储需要做的是用Key去建立一份索引,Key可以理解为文件名是该对象唯一标识符。如果用户要执行Listing,只能在这个索引里面去搜索,搜索的性能相比树形结构的查找弱很多。


文件系统的结构:树状,适用于按目录组织数据进行计算处理
对象存储的结构:扁平,适用于数据存储和直接访问
2>对象存储没有原子Rename,影响任务的稳定性和性能。
        在ETL的计算模型中,每个子任务完成会将结果写入临时目录,等到整个任务完成后,把临时目录改名为正式目录名即可。
        这样的改名操作在HDFS和其他文件系统中是原子的,速度快,而且有事务性保证。但由于对象存储没有原生目录结构,处理rename操作是一个模拟过程,会包含大量系统内部的数据拷贝,会耗时很多,而且没有事务保证。
        用户在使用对象存储时,常用文件系统中的路径写法作为对象的Key,比如"/order/2-22/8/10/detail"。改名操作时,需要搜索出所有Key中包含目录名的对象,用新的目录名作为Key复制所有的对象,此时会发生数据拷贝,性能会比文件系统差很多,可能慢一两个数量级,而且这个过程因为没有事务保证,所以过程中有失败的风险,造成数据不正确。这样看起来很细节的差异对整个任务pipeline的性能和稳定性都会有影响。
        对象存储数据最终一致性的机制,会降低计算过程的稳定性和正确性。举个例子,比如多个客户端在一个路径下并发创建文件,这是调用List API得到的文件列表可能并不能包含所有创建好的文件列表,而是要等一段时间让对象存储的内部系统完成数据一致性同步。这样的访问模式在ETL数据处理中经常用到,最终一致性可能会影响到数据的正确性和任务的稳定性。

        除了上述由于文件系统和对象存储本身差异带来的问题外,在对象存储上使用Hadoop的另一大问题,就是对象存储对于Hadoop组件的兼容性相对弱。在文章开头Hadoop架构介绍中提到了HDFS是Hadoop生态早期几乎唯一的存储选择,上层各种各样的组件都是面向 HDFS API 开发的。而到了对象存储上,数据存储的结构变了, API也变了。
        云厂商为了能够与现有的这些Hadoop组件适配,一方面需要去改造组件和云对象存储之间的connector,另一方面还需要给上层的组件去打patch,对于每一个组件都一一的去验证兼容性,这对公有云厂商来说意味着巨大的工作量。
企业如何能够享受到对象存储的强大性能,同时又兼顾文件系统的准确性?

对象存储+JuiceFS(开源云原生分布式存储系统)
        JuiceFS是一个开源的云原生分布式文件系统,为云环境设计,提供完备的POSIX、HDFS 和S3 API兼容性。使用JuiceFS存储数据,数据本身会被持久化在对象存储(如Amazon S3),对应的元数据可以按需持久化在Redis、MySQL、TiKV等多种数据库中。
       当用户想在对象存储上去进行复杂的数据计算、分析训练这些场景的时候,对象存储确实无法满足企业的需求;这也是我们去做JuiceFS的一个出发点,希望能够站在对象存储之上去补充他不擅长的部分,与对象存储一起以比较低廉的价格服务好密集性的数据计算、分析、训练这些场景。
        JuiceFS+对象存储是如何工作的呢?通过下图JuiceFS在Hadoop集群中的部署方式,简单介绍原理。
        从下面这个简单的示意图看到,YARN管理的这些执行节点上,都带一个JuiceFS Hadoop SDK,这个SDK可以保证完整兼容HDFS。图片下方可以看到,SDK它需要访问两个部分,左侧是JuiceFS Meta Engine,右侧是S3 bucket。
        Metadata engine就相当于HDFS里的NameNode,整个文件系统的元数据信息会存储在这里,元数据信息包括目录数、文件名,权限时间戳这些信息,并且相应的解决掉了HDFS NameNode扩展性 、GC这些的痛点。

        另外一边,数据存在S3 bucket里面,这里的S3bucket 等同于HDFS中的DataNode,可以将它看成一大堆海量的磁盘来用,它会管理好的数据存储和副本的相关任务。JuiceFS就是三个组件组成,JuiceFS Hadoop SDK, Metadata Engine和S3 Bucket。
相较于直接使用对象存储,JuiceFS 还有哪些优势呢?
1>HDFS 100%完整兼容。这得益于我们最初完整兼容POSIX的这个设计。POSIX API的覆盖程度以及复杂程度是大于HDFS的,HDFS在设计的时候就是去简化了POSIX,因为最先去实现复杂的 API 集,再去简化它就变得非常容易了,所以这也是JuiceFS能实现100% 实现HDFS完整兼容性的一个原因。
同时,用户可以和HDFS一起使用,无需完全替换HDFS。这也得益于Hadoop系统的设计,在一个Hadoop集群里,可以配置多个文件系统,JuiceFS和HDFS可以同时使用,并不是互相替代的关系,而是可以互相合作。这样的架构给我们我们现有的集群带来的好处是用户不用完整替代现有的HDFS集群,完整替代的工作量和风险上都太大了。用户可以结合着业务,结合着集群的情况,分步分批的去做融合。
2>元数据性能强大,JuiceFS 将元数据引擎独立出来不再依赖于S3里面的原数据性能,保证了元数据的性能。使用JuiceFS的时候,对底层对象存储的调用简化到只是get、 put、delete这三个最基础的操作,像listing, update等命令都用不到,在这样的架构下,用户就避开了对象存储元数据性能弱的问题,最终一致性这些问题也都不再存在了。
3>原子Rename,因为有独立的原数据引擎,JuiceFS也可以支持原子Rename。
4>缓存,有效提升热数据的访问性能,提供了data locality特性。缓存可以让热数据缓存到执行器worker节点本地的一些磁盘空间上。有了缓存后,会反复访问的热数据,不需要每次都通过网络去对象存储里面读数据。而且JuiceFS特意实现了HDFS特有的数据本地性的API,让所有支持数据本地性的上层组件都能重新获得数据亲和性的感知,这会让YARN把自己的任务优先调度到已经建立缓存的节点上面,综合的性能可以和存储计算耦合的HDFS相当的。
5>兼容POSIX,与机器学习、AI相关的任务应用结合方便。JuiceFS还兼容POSIX,可以和机器学习,AI相关的这些业务更便捷地融合。

总结:
        伴随着企业需求的更迭、基础技术的发展,存储和计算的架构在变,从最初的耦合到分离;实现存算分离方式多样,各有利弊。从直接将HDFS部署到云上,到使用公有云提供兼容Hadoop的方案,再到公有云+JuiceFS这样的适合在云上进行复杂大数据计算和存储的方案。对于企业来说,没有银弹,结合自身需求做架构选型才是关键。

3.5    综合考虑说明
根据市场上现有数据湖、湖仓一体技术调研情况。


3.5.1 云原生搭建数据湖存储架构
1>私有云方式 存算分离
JuiceFS开源云原生分布式存储(云对象存储独立集群) +独立计算集群 = 湖仓一体存算架构

        类似此种方式是各云服务厂商提供的数据湖方案,唯一变化的点在于JuiceFS系统,此系统 在华为、腾讯、滴普、星环等云服务商各家的数据湖产品中各不相同,会替换成其它自研系统。
        JuiceFS在阿里数据湖产品中有运用,是基于公有云上使用,且底层存储为OSS对象存储,属于深度自研产品,不一定支持私有云部署,需再进行深入咨询或试用后再作结论。

3.5.2 原生Hadoop搭建数据湖存储架构
1>物理机方式 存算一体
HDFS存储DataNode  + 计算NodeManager = 湖仓一体存储架构

2>物理机方式 存算分离
HDFS(独立存储集群DataNode)  + 独立计算集群(NodeManager) = 湖仓一体存储架构

         基于原生Hadoop HDFS的数据湖实现,带有缺陷,NameNode原生不支持横向扩展,以及存算分离架构带来的数据本地性减弱问题,首先需要解决以上问题。
         在Hadoop3.X版本开始,HDFS已支持S3a协议,代表着Hadoop生态已朝着兼容第三方对象存储系统发展,事实上在HDFS已能直接往对象存储系统写数据。
         2020年9月,Hadoop社区推出了原生分布式对象存储系统Ozone,用以解决小文件过多影响NameNode性能问题。目前来看还是一个独立的系统,比较单一,将来的发展方向是Ozone和HDFS合二为一,在同一套分布式存储系统上既能支持文件存储,又能支持对象存储。
         本人更偏好存算分离架构,此架构能让存储和计算都可以单独进行扩容,能更好的管理集群资源,提升资源利用率。最终是采用存算一体还是存算分离架构,要根据第三方数据湖产品的设计实现和实际情况来定。
        还需考虑集群运维方向,包括但不限于集群监控、高可用、扩容、升级、备份、迁移、容灾、冷热数据分级存储等内容。



【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。