浅谈iceberg的存储文件
这是我的第100篇原创文章
【前言】
上一篇文章介绍了如何通过java api对iceberg进行操作。这次我们来聊聊iceberg里的存储文件。
iceberg中的持久化存储的文件可以简单的分为数据文件和元数据文件。数据文件就是存储数据记录的文件,而元数据文件又可以分为元数据描述文件、清单列表文件(manifest list),或者根据文件名又可以称为快照文件、以及清单文件(manifest file)。三类文件通过层级关系相互关联起来。下面就分别详细介绍下文件的具体内容与格式。
【数据文件】
通常在表存储目录的data子目录下,存放的是实际数据记录的文件,文件的格式在建表时指定,默认为parquet。当然也可以指定为orc、textfile等支持的类型。
另外,在有定义分区字段的表中,数据写入时会按照分区字段的值依次创建子目录,最终的数据文件则存放在这些子目录中。
注:对于api的操作,可以自定义数据的存储路径。
【元数据文件】
该目录主要存放记录表的元数据信息的文件,可以分为如下几类:
1. $VersionID-$UUID$Extension
该文件记录表的元数据信息。在创建表的时候,会同步创建该文件,此后的每次操作都会产生新的元数据文件。
文件名中的 $VersionID为版本号,共5位长度;$UUID是通过UUID库生成的随机32位的ID,$Extension为文件的后缀,默认情况下为".metadata.json",如果对元数据指定了压缩方式,那么会在".metadata.json"的前面加上压缩类型的名称,例如"gzip.metadata.json"。
如文件名中的后缀描述一样,该文件采用json格式进行存储,下面罗列了各字段的含义:
format-version
table-uuid
location
last-updated-ms
last-column-id
schema
current-schema-id
schemas
partition-spec
default-spec-id
partition-specs
name
transform
source-id
field-id
last-partition-id
default-sort-order-id
sort-orders
properties
current-snapshot-id
snapshots
operation
added-data-files
added-records
added-files-size
deleted-data-files
delete-records
removed-files-size
changed-partition-count
total-records
total-files-size
total-data-files
total-delete-files
snapshot-log
metadata-log
元数据文件格式的版本,默认为1,代表为v1版本。
表的uuid。
元数据文件存储位置URI,通常是在hdfs中的全路径。
元数据最后更新时间
最后一个列字段的ID
表格式定义说明,属于v1版本中的必需字段,在v2版本中以下面两个字段替代。
当前表格式定义(schemas中包含的schema数组)使用的schema id。
v2格式中表格式定义说明,字段的值为一个数组,记录了历史schema的变更情况,数组中的每一项均为表schema的对象,包括类型、ID、字段数据,配合上面的current-schema-id就能知道当前版本的表格式定义是怎样的了。
"schemas" : [ {
"type" : "struct",
"schema-id" : 0,
"fields" : [ {
"id" : 1,
"name" : "id",
"required" : false,
"type" : "int"
}, {
"id" : 2,
"name" : "name",
"required" : false,
"type" : "string"
}, {
"id" : 3,
"name" : "birth",
"required" : false,
"type" : "string"
} ]
} ]
表的分区字段定义说明,同样属于v1版本中的必需字段
默认使用的分区的ID
表的分区字段定义说明,v2格式中的必须字段。字段的值为一个数组,记录了历史的分区定义,数组中的每一项均为一个分区对象,其中包括ID和分区字段说明,对于分区字段说明则又包含如下几个字段。
分区字段的名称。
分区字段的转换方式,一般来说是字段本身,即"identity"; 但可以是year、month、day、bucket等转换函数,实现不同的分区的逻辑,这里的值就是对应转换函数名。
对应schema中的filed字段的ID
分区字段定义的ID,默认从1000开始递增。
与schemas类似,配合default-spec-id字段可以知道当前的分区定义。
最后一个分区字段的ID,即对应partition-specs中分区字段的filed-id。
默认排序的ID
字段排序定义,默认为空数组
表的属性定义。
当前使用的快照ID
快照文件列表,具体值为一个数组,数组中的每一项均为一个快照信息,每个快照信息又是一个对象,包括ID(snapshot-id)、父snapshot的ID(parent-snapshot-id)、生成的时间戳(timestamp-ms)、概要信息(summary),文件清单(manifest-list或manifests)、表schema的ID(schema_id),其中概要信息包括的常见的子字段有:
本次快照引发的操作,例如append、delete、replace、overwrite。
本次快照添加的文件数,operation操作为append才有该字段
本次快照添加的记录数,operation操作为append才有该字段
本次快照添加的文件大小,operation操作为append才有该字段
本次触发快照的操作所删除的文件数,operation操作为delete才有该字段
本次触发快照的操作所删除的记录数,operation操作为delete才有该字段
本次触发快照的操作所删除的文件大小,operation操作为delete才有该字段
本次涉及变更的分区数
本次快照完成后,表中总的记录数
本次快照完成后,总的文件大小
本次快照完成后,总的文件数
本次快照完成后,总的删除文件数
"snapshots" : [ {
"snapshot-id" : 8319024382941793184,
"timestamp-ms" : 1678669777251,
"summary" : {
"operation" : "append",
"added-data-files" : "2",
"added-records" : "4",
"added-files-size" : "1978",
"changed-partition-count" : "2",
"total-records" : "4",
"total-files-size" : "1978",
"total-data-files" : "2",
"total-delete-files" : "0",
"total-position-deletes" : "0",
"total-equality-deletes" : "0"
},
"manifest-list" : "hdfs://hdfsHACluster/user/hive/warehouse/iceberg_db.db/developer/metadata/snap-8319024382941793184-1-051dc090-a770-441c-b76b-b97a591a97c7.avro",
"schema-id" : 0
} ]
snapshot的历史集合,以数组形式表示。
元数据文件(metadata.json)的历史集合,以数组形式表示。
2. snap-xx.avro
清单列表文件,也称为快照文件,每次有数据(提交)写入时触发生成。
在该文件中主要记录了清单文件记录集,文件以avro的格式进行存储,每一条记录表示一个manifest,在每个记录中最主要的字段信息为"manifest_path",标记清单文件的存储位置。
{
"manifest_path": "hdfs://hdfsHACluster/user/hive/warehouse/iceberg_db.db/developer/metadata/051dc090-a770-441c-b76b-b97a591a97c7-m0.avro",
"manifest_length": 6181,
"partition_spec_id": 0,
"added_snapshot_id": {
"long": 8319024382941793184
},
"added_data_files_count": {
"int": 2
},
"existing_data_files_count": {
"int": 0
},
"deleted_data_files_count": {
"int": 0
},
"partitions": {
"array": [{
"contains_null": false,
"contains_nan": {
"boolean": false
},
"lower_bound": {
"bytes": "2023-02-10"
},
"upper_bound": {
"bytes": "2023-03-10"
}
}]
},
"added_rows_count": {
"long": 4
},
"existing_rows_count": {
"long": 0
},
"deleted_rows_count": {
"long": 0
}
}
3. $COMMITUUID-m$COUNT.avro
(数据文件)清单信息,一个快照中可能包含了多个这样的文件信息。而在该文件中包含了涉及的多个数据文件信息。其中$COMMITUUID为提交的事务UUID,与快照文件中的COMMITUUID保持一致,$COUNT为清单文件的计数,从0开始。
该文件同样采用avro的格式进行存储,每一条记录描述一个具体的数据文件,在该记录中由三个字段组成:
status
snapshot_id
data_file
文件状态,0表示已存在、1表示新增、2表示删除
文件对应的快照ID
文件详细信息,包括了文件路径,文件格式,以及文件中一些相关的metrics信息,例如记录数、列字段、最大最小值等。
完整示例如下所示:
{
"status": 1,
"snapshot_id": {
"long": 8319024382941793184
},
"data_file": {
"file_path": "hdfs://hdfsHACluster/user/hive/warehouse/iceberg_db.db/developer/data/birth=2023-02-10/00000-0-hadoop_20230313090641_733a351d-0554-4129-a56b-861f715638b9-job_1677482770597_0127-00002.parquet",
"file_format": "PARQUET",
"partition": {
"birth": {
"string": "2023-02-10"
}
},
"record_count": 2,
"file_size_in_bytes": 988,
"block_size_in_bytes": 67108864,
"column_sizes": {
"array": [{
"key": 1,
"value": 54
}, {
"key": 2,
"value": 60
}, {
"key": 3,
"value": 103
}]
},
"value_counts": {
"array": [{
"key": 1,
"value": 2
}, {
"key": 2,
"value": 2
}, {
"key": 3,
"value": 2
}]
},
"null_value_counts": {
"array": [{
"key": 1,
"value": 0
}, {
"key": 2,
"value": 0
}, {
"key": 3,
"value": 0
}]
},
"nan_value_counts": {
"array": []
},
"lower_bounds": {
"array": [{
"key": 1,
"value": "\u0002\u0000\u0000\u0000"
}, {
"key": 2,
"value": "ma"
}, {
"key": 3,
"value": "2023-02-10"
}]
},
"upper_bounds": {
"array": [{
"key": 1,
"value": "\u0004\u0000\u0000\u0000"
}, {
"key": 2,
"value": "yuan"
}, {
"key": 3,
"value": "2023-02-10"
}]
},
"key_metadata": null,
"split_offsets": {
"array": [4]
},
"sort_order_id": {
"int": 0
}
}
} {
"status": 1,
"snapshot_id": {
"long": 8319024382941793184
},
"data_file": {
"file_path": "hdfs://hdfsHACluster/user/hive/warehouse/iceberg_db.db/developer/data/birth=2023-03-10/00000-0-hadoop_20230313090641_733a351d-0554-4129-a56b-861f715638b9-job_1677482770597_0127-00001.parquet",
"file_format": "PARQUET",
"partition": {
"birth": {
"string": "2023-03-10"
}
},
"record_count": 2,
"file_size_in_bytes": 990,
"block_size_in_bytes": 67108864,
"column_sizes": {
"array": [{
"key": 1,
"value": 53
}, {
"key": 2,
"value": 61
}, {
"key": 3,
"value": 103
}]
},
"value_counts": {
"array": [{
"key": 1,
"value": 2
}, {
"key": 2,
"value": 2
}, {
"key": 3,
"value": 2
}]
},
"null_value_counts": {
"array": [{
"key": 1,
"value": 0
}, {
"key": 2,
"value": 0
}, {
"key": 3,
"value": 0
}]
},
"nan_value_counts": {
"array": []
},
"lower_bounds": {
"array": [{
"key": 1,
"value": "\u0001\u0000\u0000\u0000"
}, {
"key": 2,
"value": "chen"
}, {
"key": 3,
"value": "2023-03-10"
}]
},
"upper_bounds": {
"array": [{
"key": 1,
"value": "\u0003\u0000\u0000\u0000"
}, {
"key": 2,
"value": "jie"
}, {
"key": 3,
"value": "2023-03-10"
}]
},
"key_metadata": null,
"split_offsets": {
"array": [4]
},
"sort_order_id": {
"int": 0
}
}
}
【场景分析】
按照以往文章的惯例,我们还是通过一个实际场景,包括建表、到插入数据、修改列、删除分区的数据、删除快照等操作,元数据文件的变更等操作,对上面文件中一些关键字段进行剖析。
1. 创建表
表创建后,将元数据信息写入metadata.json文件中,但此时由于还没有数据,因此不会写入快照信息、数据清单文件。
2. 插入数据
该操作完成后写入了新的元数据文件(注意,ID自增),快照文件、数据清单文件、以及实际的数据文件。
注意:建表时有指定分区,因此不同的记录数据存放在对应分区目录下。
3. 对表schema进行变更,新增一个列字段
由于没有数据的变化,因此只会新增一个元数据文件。
4. 再次插入数据
同样,数据是按照分区存放在不同的目录下,同时有新的清单文件记录本次操作新增的文件,而新的快照文件则同时引用两个清单文件,记录表的全量数据。元数据文件中记录了所有的快照信息,同时也记录当前使用的快照ID。
5. 删除指定分区的数据
对于数据的更新,默认采用写入合并记录的方式,因此,新的快照中只记录了两个新的清单文件,在这两个清单文件中分别记录不同的数据文件以及文件的状态。
6. 再次写入数据
流程和第4步操作一致,在上一个快照的基础上新增相关信息。
7. 仅保留当前快照
删除快照的同时,对于(当前保留的快照中)没有引用到的清单文件、以及标记为删除的数据文件,都会一起进行删除。
【总结】
简单小结一下,本文主要介绍了iceberg持久化的几个文件,以及文件的存储内容与格式,以及相互之间的关联关系,最后通过一个实际例子,分析了元数据文件的组织与变更。了解了这些内容,将有助于理解iceberg的数据读写流程,以及其他相关逻辑。
当然,文章中也提到了元数据文件格式的版本(v1,v2),不同版本对使用上也会有所区别。后面再单独讲解。
好了,这就是本文的全部内容,如果觉得本文对您有帮助,请点赞+转发,如果觉得有不正确的地方,也可以拍砖指点,最后,欢迎加我微信交流~
本文分享自微信公众号 - 陈猿解码(gh_383bc7486c1a)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
解决创新业务的三大架构难题,央广购物用对了这个关键策略
导读 央广购物借助云原生技术,解决了品小美这类创新业务普遍面临的资源预估难、运维成本高以及故障定位慢等难题。 背景介绍 央广购物系广电总局批准核发的,依托于中央广播电视总台的全国性电视购物公司。央广购物以电视直播和网络直播为基础,持续构建内容电商生态和服务能力。 央广购物响应新零售的业务趋势,推出了拼团直播带货的“品小美”子品牌,以微信小程序为依托,通过主播团长拼团的模式,推动电商业务的发展。“品小美”一方面能够为电视购物会员带来更丰富便捷的购买渠道与更多价格实惠的商品,另一方面也能帮助电视购物频道实现用户沉淀,搭建私域流量池,提升复购率。 传统架构下的业务痛点 品小美这类新型电商业务有几个特点: 新商品上架或者搞活动的时候抢购人数特别多,订单量突增比较明显; 半夜等业务低峰期几乎无人使用; 新功能上线要求快速敏捷; …… 在这样的业务特点下,如果使用传统的服务器部署应用,会遇到很多问题。 主要有以下4个痛点: 首先,资源既有浪费也有不足的情况。比如业务高峰期来不及扩容,导致资源不足。当业务高峰期过去,没有及时缩容,导致资源冗余,资源利用率不高,造成了一定的资源浪费。 其次,运维成本高...
- 下一篇
大数据计算引擎 EasyMR:拥抱开源,引领技术创新
身处数字经济时代,随着大数据应用越来越广泛,越来越多的企业和组织开始关注大数据基础平台的建设和运营。在认识到其的重要性之后,如何具体着手搭建或采购大数据基础平台成为下一步需要解决的问题。 在大数据基础平台中,大数据组件是非常重要的一部分,包括数据存储、数据处理、数据分析、数据可视化等。在选择大数据组件时,我们常常在闭源组件和开源组件选择中反复纠结。 本文将从优势、劣势两个维度分析开源组件、闭源组件对大数据基础平台建设的影响,并结合袋鼠云自研的大数据计算引擎 EasyMR 的实践经历进行分享。 开源组件 在大数据领域,开源组件已经成为了构建大数据平台的重要基石。例如 Hadoop、Spark、Hive、HBase、Kafka、Storm、Flink 等开源软件已经成为了大数据处理和分析的主要工具。 这些开源组件不仅提供了高效、可扩展、可靠的大数据处理和存储能力,而且还促进了生态系统的发展,形成了庞大的开发社区和丰富的第三方工具及应用程序。 优势 ● 免费 开源组件一般都是免费的,其源代码是公开的,任何人都可以下载、使用、修改和分发,这将极大降低企业的开发和建设成本。 ● 灵活性 由于源代...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19