调研报告-基于 Iceberg 构建湖仓一体平台调研
一、背景
我们使用 Iceberg 构建湖仓一体平台的初衷是希望解决业务方在使用 Hive 数仓时的一些痛点。主要包括以下几大方面:
(1)现有的数据同步只支持全量同步,同步大表速度慢。
(2)Hive 的时效性不好,即使使用 FIink 流式的引擎写入,延迟也会在小时级别。
(3)Hive扫描数据效率低。Hive查一个分区的话是需要将分区下所有文件都扫描一遍然后进行分析,而实际上我可能只对某些文件感兴趣。所以hive全量文件扫描相对低效。
(4)Lambda 架构建设的实时数仓存在较多问题。如维护两套代码运维监控成本高、实时离线数据对不上、实时链路kafka无法存储海量数据,无法基于OLAP查询kafka中的数据。
(5)不能友好支持高效更新场景。HDFS只支持追加写,不支持更新。
(6)不可靠的更新操作。在ETL过程中执行insert overwrite之类操作,这种操作会先把相应分区的数据删除,再把生成的文件加载到分区中。在移除文件时,读取的话会发生异常报文件不存在。
我们希望我们的湖仓一体平台能够解决这些痛点,我们希望可以实现:
(1)首先,湖仓平台要是互联互通的,要支持多引擎的访问。
(2)第二,查询要高效,以满足交互式分析的要求。
(3)第三,使用要尽可能的便捷,尽可能降低业务方的门槛。
(4)第四,基于HDFS海量存储实现准实时数仓,存储离线和实时数据。
(5)最后,支持高效Upsert、支持Schema变更
二、数据湖Iceberg介绍
1、Iceberg
Iceberg 是一个由 Netflix 开发开源的、用于庞大分析数据集的表格式。它是基于计算层(flink、spark)和存储层(orc、parquet)的一个中间层,它并不定义数据存储方式,而是定义了数据、元数据的组织方式,向上提供统一的“表”的语义,我们可以把它定义成一种“数据组织格式”,对标 Hive 的表设计。特性主要是代码抽象程度高,不绑定任何的引擎。
现在国内的实时数仓建设围绕 Flink 的情况会多一点。所以能够基于 Flink 扩展生态,是我们选择 Iceberg 一个比较重要的点。
2、表格式Hive VS Iceberg
特性
|
Hive
|
Iceberg
|
---|---|---|
数据规模 | PB级 | PB级 |
成本 | 廉价 | 廉价 |
修改粒度 | 分区级 | 文件级更新 |
提交机制 | 提交后重命名数据文件,复制数据 | 无需重命名 |
重新计算数据 | 修改涉及到的所有分区数据 | 仅快照间增量数据重复 |
时效性 | 离线。天/小时级别 | 近实时,分钟级 |
扫描时执行计划列举目录调用次数list接口 | O(N)。N=命中分区数量 | 常数级。元文件+Snapshot文件 |
分区过滤 | 分区值 | 分区值、隐式分区表现有字段转换而来 |
文件过滤 | 无 | 列值基于文件统计值和字典过滤 更高效 |
版本控制 | 无 | 可回滚到特定版本 |
行级更新 | 无 | Format V2格式支持 |
3、Iceberg优点
分类 | 优点简述 | 详细描述 |
---|---|---|
更易于管理与使用 | 表结构变更方便 | 以前 Hive 变更表结构会有很多限制,因为它的表结构和文件里面的数据格式是一种松耦合的方式,所以只能安全的在表上增加字段,或增加数据长度,这些操作一定要保证能向前兼容。在实际的生产场景中,业务系统可能有很复杂的表结构变更需求,比如删除字段、重命名等。以前在大数据系统里面要去应对这样的场景,是比较麻烦的。Apache Iceberg 很好的支持了变更-多个schema |
隐藏分区 | Iceberg 仍然沿用了数据库分区的使用习惯。Hive 表把分区定义成了一种特殊的字段,在查询的时候,一定要在分区字段值上加上条件,才能起到分区过滤。隐式分区回归了传统数据库的使用习惯,也就是分区是基于表上已有字段的做额外划分规则,而不再是一些额外定义的字段。在生成数据和读取数据的时候,不再需要指定分区字段的值,它们会基于依赖的字段自动计算而来。 | |
分区变化 | Hive 的分区发生变化,就要对整个表进行重写,才能用到新的分区。Iceberg 的分区规则发生变化,后写的数据就会应用后面的分区规则,前面的数据不需要重写,表的管理更加友好。 | |
Time travel | 时间旅行使得用户可以看到任意时刻表中的数据。比如今天下午两点钟的时候,也可以看今天上午 10 点钟的数据,通过 time travel,可以方便地定位到当初的数据的版本,可以完成对历史数据的分析。 | |
Rollback | 支持把数据回退到历史的版本,当出现错误的数据写入的时候,rollback 会变得非常有用。 | |
流批统一 | 流批写入\读取 | Hive 是完全面向于离线去设计的,一个表同时只有写或者读,写和读都是批量操作。Iceberg 在读写接口上都做了流和批的兼容,流批统一的特性解决了生产场景中的 Lambda 架构带来的流批分割的问题。流批一体的基础是存储层面上是流批统一,基于此计算层面才能向着使用一套代码完成流批开发的目标。 |
隔离能力 | 相比离线写入,流式处理的写入变得非常频繁,随之而来一个表上同时有读写操作变得非常普遍,良好的隔离能力能够保证在表上并发的读写操作都能得到正确的结果。Iceberg 多版本的机制提供了 serialization 最严格的隔离级别。 | |
并发写入 | 除了读写上的并发,流批一体后流与批也会同时写入一张表,Iceberg 基于乐观并发控制逻辑,在并发写入并无数据冲突时能够同时提交成功。 | |
高效与易扩展 | 扫描快 | 元数据不再存储在单点的服务器上,而是分散的存储在不同的元数据文件里,并行的对元数据进行处理可以获得更好的读取性能。 |
高级过滤 | 在元数据层面上增加了很多统计信息,比如说文件中每个字段的最大/最小值、这些元数据能够在数据查询时更快的过滤掉无需扫描的数据。 | |
适用于任何云存储 | Iceberg 在研发之初就以支持各种存储设备为目标,比如说各种分布式文件系统或者对象存储。Iceberg 在访问云上的对象存储时,直接使用对象存储的 API,无需包装或转换成 HDFS 协议,最大可能减少性能衰减,能得到云上最好的性能。 | |
4、Iceberg缺点
分类
|
缺点简述
|
详细描述
|
---|---|---|
需要清除过期文件 |
Iceberg 有很多的历史版本,会占用大量的存储资源,虽然它有一定的价值,但是也应该得到定期的清理。比如设定最多保留三天,超过三天数据就应该被清理。网易开源Arctic的Expire Snapshots 就是实现这样的操作。 |
|
需要清理垃圾文件 | 计算引擎在写入 Iceberg 的时候,如果它的写入任务失败了,可能会残留一些垃圾文件在那边。这些文件并没有提交进去,也不会影响到这个表的正确读取,但是浪费了一些存储资源。这些数据应该在后面定期地去做数据清理,把浪费的存储资源给释放出来。 | |
需要合并数据文件 |
(1)合并碎片文件。在实时场景下,这个非常重要,实时场景下会频繁地往表中提交数据,这样就会产生很多小文件,这些小文件需要进行治理,以提升表上查询性能并减少对存储系统的压力。 (2)减少数据冗余。在使用了行级更新的场景下,删除操作通过独立的 delete file 文件来标记,这就会造成数据冗余,冗余数据过多会大大降低表上的查询性能。故需要通过合并数据文件操作把 delete 文件和 data file 做合并。 |
|
需要重写元数据 | 当表上的提交越来越多,manifest file 也可能会越来越多,过多的元数据文件同样会影响表上的读取性能,重写元数据操作会对元数据进行重新整理。 |
三、数据湖Iceberg核心原理
1、Catalog(管理表)
Iceberg 使用 catalog 来管理所有的表,catalog 中存储了当前有哪些表,以及这些表如何按照 namespace 拆分。Iceberg 提供了丰富的 catalog 实现,也提供了API 方便你来扩展自己的 catalog 实现,以便你把表的元数据存储在任何想存储的地方。
(1)HadoopCatalog,直接使用文件目录管理表信息。
(2)HiveCatalog,将表的元数据信息存储在 Hive Metastore中。
(3)JdbcCatalog,把表的元数据信息存储在数据库中。
glink01服务执行gomysqltx_testhive
Iceberg数据库信息
Iceberg表信息
建表语句
CREATE TABLE spark_catalog.iceberg_test.test3 (
`id` INT,
`empcode` STRING)
USING iceberg
LOCATION 'ofs://usr/hive/warehouse/iceberg_test.db/test3'
TBLPROPERTIES(
'current-snapshot-id' = '2379142480402238200',
'format' = 'iceberg/parquet')
|
2、Metadata(存储表内部的元数据)
(1)Snapshots:每次数据写入都会生成一个新的 snapshot ,而读取是基于某个确定的 snapshot 来操作的,所以写入和读取可以并发进行,互不影响。在写入的时候,也可以通过 snapshot 去判断出是不是和其他的写入发生了冲突,因为 snapshot 是统一的一条线,提交的时候可以通过这条线做冲突的判断。snapshot 存储有个很重要的概念叫 current snapshot,是当前最新的版本,如果要读取历史版本的数据,也可指定时间或者 snaoshot id 来完成 time travel。
对应HDFS文件:/usr/hive/warehouse/iceberg_test.db/test3/metadata/00001-063d2d17-58df-41c4-af5f-899add03281c.metadata.json
(2)Manifest list:存储的是 manifest file 列表,用来链接大量的manifest file文件。
对应HDFS文件(带snap前缀):/usr/hive/warehouse/iceberg_test.db/test3/metadata/snap-2379142480402238200-1-5183f5e9-7516-4ee8-a454-9c6263b7e624.avro
(3)Manifest file:存储当前快照下的所有 data file,每行存储一个文件的信息。Manifest file 使用 avro 格式,为了提升读取文件信息的并发度,manifest file 一般会有多个。
对应HDFS文件(不带snap前缀):/usr/hive/warehouse/iceberg_test.db/test3/metadata/5183f5e9-7516-4ee8-a454-9c6263b7e624-m0.avro
(4)Schemas:表所有的历史的 schema 信息都会存储在这里,每个 schema 会有一个自己的 schema id,每个写入的文件都有自己的 schema id 信息,当表结构发生变更,新老文件用的是不同的 schema id,这样就不需要把所有的数据重写。
(5)Partition specs:分区配置,核心是各种 transform。Hive 的分区是配置分区字段,然后写入和读取的时候要读写这些分区字段。Iceberg 则是在已有的表字段上配置各种转换函数,比如表里有一个 timestamp 字段,需要用 timestamp 字段按天做分区,那就在 timestamp 字段上使用天的转换函数。写数据的时候,不需要再写入额外转换之后天的信息,只需要写入 timestamp,Iceberg 内部就会把这 timestamp 字段的值转换成分区的天的信息,在查询的时候,只要在 timestamp 字段做一个范围查询就能实现高效的基于分区的数据过滤。
3、Iceberg formatV2
Iceberg 现阶段有两个版本的 table format,面向大体量数据分析的 format version 1 与 提供行级更新能力的 format version 2, format version 2 相较于 format version 1,功能上有较多增强。
(1)Row-level Delete:行级更新删除能力。以前数据写入都是文件级别的,更新文件里面的部分数据,需要对这个文件进行完整的重写,把老的文件删除,把新的文件写进去。这种方式的缺点是开销大,Flink 流式场景下基本不可用。format V2 新增行级更新删除,实现方式是新增了一种 delete file 文件类型,文件里记录了表中需要删除的数据信息。比如去做一个 update 操作,只需去找出这次 update 需要更新的数据在原来的文件里面的什么位置并记录到 position delete file 中新数据插入新文件即可。或者直接地将这次 update 的条件信息记录在 equality delete file 中,再将更新后的内容写入新的 data file 中,将产生的 delete file 和 data file 提交到表中即完成了本次的 update 操作。
(2)Position delete file:记录删除一行数据在 文件中的位置。通过被删除数据的位置信息来标记删除内容的 delete file,position delete file 记录被删除数据的 file path 和 position,并且在文件内所有数据会按照顺序排序后写入,这样在与 data file 合并时会有更好的合并性能。
(3)Equality delete file:记录删除数据的条件信息。通过被删除数据的等值条件来标记输出内容的 delete file,比如 ID = 5 的数据要删除, Equality delete file存储的就是 ID = 5 这样的信息,在 Merge-on-Read 时就会把 ID = 5 的数据全部过滤掉。Iceberg Flink connector 当前会混合写入 position delete file 和 equality delete file。
(4)File sequence:标记文件的版本。每次写入后递增;delete file 只对比自己 file sequence 更小的文件起作用。比如 file sequence 等于 2 的 delete file 只对 file sequence 小于等于 1 的 data file 起作用。每次写入的文件拥有相同的 file sequence,且随着写入顺序递增。
(5)Rewrite files (compaction):数据合并。把要删除的数据真实地删除掉,得到删除之后的 datafile,这样的 datafile 读取更高效。这个叫做 Iceberg 的 rewrite 过程。需要注意 rewrite 不会修改文件的文件版本信息。
四、Table Format对比
左侧图是一个抽象的数据处理系统,右侧是对应的现实中的组件
1、抽象层Table Format介绍
对于抽象的table format,主要包含4个层面的含义,分表是schema 定义、表中文件的组织形式、表相关统计信息、表的读写 API 实现。详细介绍如下:
表 schema: 定义了一个表支持字段和类型,比如 int、string、long 以及复杂数据类型等。
表中文件组织形式:最典型的是 Partition 模式,是 Range Partition 还是 Hash Partition。
表相关的统计信息:Metadata 数据统计信息。
表的读写 API 实现:封装表的读写 API,层引擎通过对应的API读取或者写入表中的数据
2、hive metastore和Iceberg 的Table Format对比
(1)表schema对比(都支持常用类型)
都支持int、string、decimal、timestamp、map常用数据类型
https://iceberg.apache.org/docs/latest/schemas/
(2)表中文件组织形式(partition的实现方式不同)
Hive Metastore:metastore中的partition不能是表字段。partition 本质上是一个目录结构,不是用户表中的一列数据。基于metastore用户想定位到一个 partition 下的所有数据,首先需要在 metastore 中定位出该 partition 对应的所在目录位置信息,然后再到 HDFS 上执行list命令获取到这个分区下的所有文件,对这些文件进行扫描得到这个 partition 下的所有数据。
Iceberg:Iceberg中的partition就是表中的一个字段。Iceberg 中每一张表都有一个对应的文件元数据表(Manifest file文件),文件元数据表中每条记录表示一个文件的相关信息,这些信息中有一个字段是 partition 字段,表示这个文件所在的 partition。
对比总结:Iceberg 表根据 partition 定位文件相比 metastore 少了一个步骤,就是根据目录信息去 HDFS 上执行 list 命令获取分区下的文件。对于一个二级分区的大表来说,一级分区是小时时间分区,二级分区是一个枚举字段分区,假如每个一级分区下有30个二级分区,那么这个表每天就会有24 * 30 = 720个分区。基于 Metastore 的 partition 方案,如果一个 SQL 想基于这个表扫描昨天一天的数据的话,就需要向 Namenode 下发720次 list 请求,如果扫描一周数据或者一个月数据,请求数就更是相当夸张。这样,一方面会导致 Namenode 压力很大,一方面也会导致 SQL 请求响应延迟很大。而基于 Iceberg 的 partition 方案,就完全没有这个问题。如二级分区(hour='12','action'='click')可以根据筛选直接找到文件
(3)表相关的统计信息(统计粒度不同)
Hive Metastore :一张表的统计信息是表/分区级别粒度的统计信息,如一张表的记录数量、平均长度、为null记录数量、最大值、最小值等。
Iceberg:统计信息精确的文件粒度,每个数据文件都会记录所有列的记录数量、平均长度、最大值、最小值等。
对比总结: Iceberg文件粒度统计信息对于查询中谓词过滤(where条件)会更有效。
(4)读写Api(实现方式和功能不同)
Metastore :上层引擎写好一批文件,调用metastore的add partition接口将这些文件添加到某个分区下。
Iceberg:上层业务写好一批文件,调用Iceberg的commit接口提交本次写入,形成一个新的snapshot快照。这种提交方式保证表的ACID语义。同时基于快照提交可以实现增量拉取。
对比总结:metastore只能添加一批文件加分区。Iceberg可以增量提交。
整体对比Iceberg相对于Metastore的优势
新partition模式:避免查询时多次调用namenode的list方法,降低namenode压力,提升查询性能。
新metadata模式:文件级别的统计信息可以更快的根据where条件进行文件过滤。可以减少扫描文件数,提高查询性能。
新的Api模式:存储流批一体。支持流式写入-增量拉取(满足批量读取和增量订阅)。支持流批同时读写同一张表,防止insert overwrite操作时读取报FileNotFoundException异常。
五、Iceberg表优化
1、建表时增加排序
2、建表时增加索引(基数高的字段 如:id)索引类型:
(1)BloomFilter:计算比较简单,占用空间也比较小。存在 false positive 的问题,只支持等值的查询。
(2)Bitmap:功能更强大,支持等值和范围查询,匹配更精准,更精准是因为可以对多个条件匹配到的数据进行交并补计算,同时它返回的行号也可以帮助进一步 skip 数据。Bitmap 的缺点是占用空间比较大,尤其是对一些高基数的字段,创建 Bitmap 索引,可能加载索引的时间已经超过了过滤掉数据所节约的时间,甚至会产生一些负向的效果。
(3)TokenBloomFilter、NgramBloomFilter,TokenBitmap、NgramBitmap:是针对 token 的索引,是为日志场景设计的。相当于对日志做一些分词的操作。分词完成以后,构建 BloomFilter 或者 Bitmap 这样的索引。TokenBloomFilter 和 TokenBitmap 针对的是英文的分词,Ngram 针对的是中文的分词。
六、数据同步链路
1、现在同步链路
2、Iceberg流批一体同步
七、待测试内容点
1、spark建表-已完成
2、hive\spark插入数据-已完成
3、hive、spark新增数据到iceberg-已完成
4、hive、spark查询数据-已完成
5、iceberg元数据存储位置查看(hdfs和hive表),元数据管理方式-已完成
6、java、flink实时方式导入数据到iceberg
7、数据同步工具waterdrop、datax离线导入mysql表数据到iceberg
8、腾讯云数据集成工具导入mysql数据到iceberg
9、数据排序写入是否提高查询速度。Z-ORDER 、 Hibert(B站优先使用),非整形字段用Boundary Index,最高4个排序字段。因写入时排好序,根据常用字段查询、抽取时扫描文件少,更快命中文件
10、Iceberg表创建索引(基数高的字段--基本不重复如id、user_id),加快速度。
11、Flink到Iceberg时小文件异步合并测试。
11、Spark到Iceberg时小文件异步合并测试。
12、Iceberg数据上和元数据上怎么治理。
13、数据上是列式存储还是行存储,用的Parquet列式存储格式,但是查询数据文件多列数据都可以查到。可以查看Parquest文件原理。
https://zhuanlan.zhihu.com/p/538163356
https://blog.csdn.net/qq_24434251/article/details/124493822
Header:每个 Parquet 的首尾各有一个大小为 4 bytes ,内容为 PAR1 的 Magic Number,用来标识这个文件是 Parquet 文件。
Data Block:中间的 Data Block 是具体存放数据的区域,由多个行组(Row Group)组成
行组 (Row Group):是按照行将数据在物理上分成多个单元,每一个行组包含一定的行数。 比如一个文件有10000条数据,被划分成两个 Row Group,那么每个 Row Group 有 5000 行数据。
列块(Column Chunk):在每个行组(Row Group)中,数据按列连续的存储在这个行组文件中,每列的所有数据组合成一个 Column Chunk(列块),一个列块拥有相同的数据类型,不同的列块可以有不同的压缩格式。
Page:在每个列块(Column Chunk)中,数据按 Page 为最小单元来存储,Page 按内容分为 Data page 和 Index Page。
Footer:包含非常重要的信息,包括Schema和每个 Row Group 的 Metadata
14、如何把Iceberg表数据固定。因Iceberg是实时写入,如当天10月21日下游使用了Iceberg数据,在10月30日下游回溯21日历史数据,还想用21日的数据。 抽数据到hive分区是其中一个方法,还有没有其它方式。
15、***Iceberg表可以单独开项目方便管理,也可以和现在项目结合,同一个库下既有hive也有Iceberg表,需要和数仓讨论下用法。前期只做数据同步可以使用单独项目去管理同步任务,同一张表只同步一份。
16、Iceberg有哪些快照可以用,是否可以整点|天生成快照
17、腾讯Iceberg表管理工具
18、实时在写担心有问题,历史每天零时跑覆盖snapshot。有咩有问题
参考文章:
1、数据湖Iceberg官网
https://iceberg.apache.org/docs/latest/
2、秒级响应!B站基于 Iceberg 的湖仓一体平台构建实践
https://www.51cto.com/article/755761.html
3、网易:Flink + Iceberg 数据湖探索与实践
https://developer.aliyun.com/article/776257
4、如何使用 Arctic 更好地玩转 Apache Iceberg
https://www.6aiq.com/article/1683096351927
5、索引使用-非紧急重要-待看
https://www.51cto.com/article/755368.html
6、数据户连篇文章-数据湖(十一):Iceberg表数据组织与查询
https://cloud.tencent.com/developer/article/2038111
7、爱奇艺数据湖平台建设实践
https://www.51cto.com/article/756677.html
8、avro格式详解
https://cloud.tencent.com/developer/article/2229382
9、Iceberg使用和管理文章-连载
https://www.cnblogs.com/fantongxue/p/16392460.html
10、华为Iceberg文章连载
https://bbs.huaweicloud.com/blogs/365635
11、数据湖Iceberg技术在小米的落地与场景应用
https://z.itpub.net/article/detail/C3552815896448F05CD8A5DE8F739667
12、谈一谈Apache Iceberg的数据读取过程
https://zhuanlan.zhihu.com/p/648195366
13、Iceberg数据湖介绍
https://www.sqlboy.tech/pages/19baa8/
14、Spark读写Iceberg在腾讯的实践和优化
https://www.toutiao.com/article/7159789145374753295/
15、DLC 原生表核心能力
https://cloud.tencent.com/document/product/1342/101854
16、Hive+Spark+Flink集成Iceberg功能POC
https://juejin.cn/post/7239309010570313787
17、Spark 合并 Iceberg 小文件内存溢出问题定位和解决方案
https://xie.infoq.cn/article/50259945d7663d7194a5e2763
18、实践数据湖iceberg 第三十课 mysql->iceberg,不同客户端有时区问题
https://blog.csdn.net/spark_dev/article/details/124302929