前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈iceberg的存储文件

浅谈iceberg的存储文件

作者头像
陈猿解码
发布2023-03-21 13:23:59
1.7K0
发布2023-03-21 13:23:59
举报
文章被收录于专栏:陈猿解码陈猿解码

这是我的第100篇原创文章

【前言】

上一篇文章介绍了如何通过java api对iceberg进行操作。这次我们来聊聊iceberg里的存储文件。

iceberg中的持久化存储的文件可以简单的分为数据文件和元数据文件。数据文件就是存储数据记录的文件,而元数据文件又可以分为元数据描述文件、清单列表文件(manifest list),或者根据文件名又可以称为快照文件、以及清单文件(manifest file)。三类文件通过层级关系相互关联起来。下面就分别详细介绍下文件的具体内容与格式。

【数据文件】

通常在表存储目录的data子目录下,存放的是实际数据记录的文件,文件的格式在建表时指定,默认为parquet。当然也可以指定为orc、textfile等支持的类型。

另外,在有定义分区字段的表中,数据写入时会按照分区字段的值依次创建子目录,最终的数据文件则存放在这些子目录中。

注:对于api的操作,可以自定义数据的存储路径。

【元数据文件】

该目录主要存放记录表的元数据信息的文件,可以分为如下几类:

1. VersionID-UUID

该文件记录表的元数据信息。在创建表的时候,会同步创建该文件,此后的每次操作都会产生新的元数据文件。

文件名中的 VersionID为版本号,共5位长度;UUID是通过UUID库生成的随机32位的ID,

如文件名中的后缀描述一样,该文件采用json格式进行存储,下面罗列了各字段的含义:

  • format-version

元数据文件格式的版本,默认为1,代表为v1版本。

  • table-uuid

表的uuid。

  • location

元数据文件存储位置URI,通常是在hdfs中的全路径。

  • last-updated-ms

元数据最后更新时间

  • last-column-id

最后一个列字段的ID

  • schema

表格式定义说明,属于v1版本中的必需字段,在v2版本中以下面两个字段替代。

  • current-schema-id

当前表格式定义(schemas中包含的schema数组)使用的schema id。

  • schemas

v2格式中表格式定义说明,字段的值为一个数组,记录了历史schema的变更情况,数组中的每一项均为表schema的对象,包括类型、ID、字段数据,配合上面的current-schema-id就能知道当前版本的表格式定义是怎样的了。

代码语言:javascript
复制
"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"
    } ]
  } ]
  • partition-spec

表的分区字段定义说明,同样属于v1版本中的必需字段

  • default-spec-id

默认使用的分区的ID

  • partition-specs

表的分区字段定义说明,v2格式中的必须字段。字段的值为一个数组,记录了历史的分区定义,数组中的每一项均为一个分区对象,其中包括ID和分区字段说明,对于分区字段说明则又包含如下几个字段。

  • name

分区字段的名称。

  • transform

分区字段的转换方式,一般来说是字段本身,即"identity"; 但可以是year、month、day、bucket等转换函数,实现不同的分区的逻辑,这里的值就是对应转换函数名。

  • source-id

对应schema中的filed字段的ID

  • field-id

分区字段定义的ID,默认从1000开始递增。

与schemas类似,配合default-spec-id字段可以知道当前的分区定义。

  • last-partition-id

最后一个分区字段的ID,即对应partition-specs中分区字段的filed-id。

  • default-sort-order-id

默认排序的ID

  • sort-orders

字段排序定义,默认为空数组

  • properties

表的属性定义。

  • current-snapshot-id

当前使用的快照ID

  • snapshots

快照文件列表,具体值为一个数组,数组中的每一项均为一个快照信息,每个快照信息又是一个对象,包括ID(snapshot-id)、父snapshot的ID(parent-snapshot-id)、生成的时间戳(timestamp-ms)、概要信息(summary),文件清单(manifest-list或manifests)、表schema的ID(schema_id),其中概要信息包括的常见的子字段有:

  • operation

本次快照引发的操作,例如append、delete、replace、overwrite。

  • added-data-files

本次快照添加的文件数,operation操作为append才有该字段

  • added-records

本次快照添加的记录数,operation操作为append才有该字段

  • added-files-size

本次快照添加的文件大小,operation操作为append才有该字段

  • deleted-data-files

本次触发快照的操作所删除的文件数,operation操作为delete才有该字段

  • delete-records

本次触发快照的操作所删除的记录数,operation操作为delete才有该字段

  • removed-files-size

本次触发快照的操作所删除的文件大小,operation操作为delete才有该字段

  • changed-partition-count

本次涉及变更的分区数

  • total-records

本次快照完成后,表中总的记录数

  • total-files-size

本次快照完成后,总的文件大小

  • total-data-files

本次快照完成后,总的文件数

  • total-delete-files

本次快照完成后,总的删除文件数

一个实际实例如下所示

代码语言:javascript
复制
"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-log

snapshot的历史集合,以数组形式表示。

  • metadata-log

元数据文件(metadata.json)的历史集合,以数组形式表示。

2. snap-xx.avro

清单列表文件,也称为快照文件,每次有数据(提交)写入时触发生成。

在该文件中主要记录了清单文件记录集,文件以avro的格式进行存储,每一条记录表示一个manifest,在每个记录中最主要的字段信息为"manifest_path",标记清单文件的存储位置。

代码语言:javascript
复制
{
  "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-mCOUNT.avro

(数据文件)清单信息,一个快照中可能包含了多个这样的文件信息。而在该文件中包含了涉及的多个数据文件信息。其中COMMITUUID为提交的事务UUID,与快照文件中的COMMITUUID保持一致,COUNT为清单文件的计数,从0开始。

该文件同样采用avro的格式进行存储,每一条记录描述一个具体的数据文件,在该记录中由三个字段组成:

  • status

文件状态,0表示已存在、1表示新增、2表示删除

  • snapshot_id

文件对应的快照ID

  • data_file

文件详细信息,包括了文件路径,文件格式,以及文件中一些相关的metrics信息,例如记录数、列字段、最大最小值等。

完整示例如下所示:

代码语言:javascript
复制
{
  "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),不同版本对使用上也会有所区别。后面再单独讲解。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-03-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陈猿解码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档