数据湖大数据数据存储【免费下载链接】icebergApache Iceberg项目地址https://gitcode.com/gh_mirrors/icebe/iceberg点击查看免费下载Apache Iceberg 的视图规范View Spec定义了与表格式Table Format同等地位的统一视图元数据格式让视图可以像表一样被共享、版本化与回滚。本文以仓库中的 format/view-spec.md 规范文档为主体结合core/src/main/java/org/apache/iceberg/view/下的源码实现与 docs/docs/view-configuration.md 配置说明完整讲解视图元数据 JSON 格式的每个字段、版本version、表示representation与版本日志version-log机制并给出可复现的元数据文件示例。读完本文你将能够读懂任意一份 Iceberg 视图元数据文件、理解视图提交与回滚的底层原理并掌握视图相关属性的配置方法。背景与动机为什么视图也需要统一格式大多数计算引擎例如 Trino、Apache Spark都支持视图View。视图是一种逻辑表可以被后续查询引用视图本身不包含任何数据而是保存一段查询语句每次被引用时都会重新执行。问题在于每个引擎都以私有格式将视图元数据存储在各自选择的元数据存储metastore中。因此即使多个引擎共享同一个 metastore 和存储系统从一个引擎创建的视图也很难被另一个引擎直接读取或修改。这与 Iceberg 表格式致力于解决的跨引擎表共享问题如出一辙。Iceberg 视图规范的目标就是提供一种与表格式并列的通用视图元数据格式使视图能够在不同引擎之间无缝共享。本文档即为该规范的权威定义。总体设计视图元数据的存储与检索视图元数据的存储方式完全镜像 Iceberg 表元数据的存储与检索方式视图元数据维护在**元数据文件metadata files**中对视图状态的任何修改都会生成一个新的视图元数据文件并通过**原子交换atomic swap**完全替换旧文件与 Iceberg 表一样这个原子交换被委托给按名称管理表/视图的metastore视图元数据文件记录视图的 schema、自定义属性、当前与历史版本以及其他元数据。每个元数据文件都是自足self-sufficient的它包含最近若干版本的完整历史因此可以用于将视图回滚到之前的版本。元数据位置与读写并发原子交换是实现视图原子变更的基础**读者Readers**使用加载视图元数据时处于当前状态的版本在刷新并获取新的元数据位置之前不受其他变更的影响**写者Writers**乐观地创建视图元数据文件假定在提交之前当前元数据位置不会被改变写者完成更新后通过将视图的元数据文件指针从基础位置base location交换到新位置来完成提交。从源码结构看这一机制对应 ViewOperations.java 与 BaseViewOperations.java 中的元数据读写与提交逻辑与 Iceberg 表的TableOperations设计保持一致。术语Schema模式视图中字段的名称与类型。Version版本视图在某个时间点的状态。视图元数据View Metadata视图版本元数据文件包含以下字段要求字段名描述requiredview-uuid标识视图的 UUID在视图创建时生成。实现必须在刷新元数据后若视图 UUID 与期望的 UUID 不匹配时抛出异常requiredformat-version视图格式的整数版本号必须为1requiredlocation视图的基础位置base location用于构造元数据文件位置requiredschemas已知 schema 的列表requiredcurrent-version-id视图当前版本的 IDversion-idrequiredversions已知版本的列表 [1]requiredversion-log版本日志条目列表记录每次current-version-id变更的时间戳与version-idoptionalproperties字符串到字符串的视图属性映射 [2]注需要保留的版本数量由视图属性version.history.num-entries控制。属性用于comment等元数据以及影响视图维护的设置不应用于存储任意元数据。源码中的字段定义这些字段在源码中有直接对应实现。在 ViewMetadata.java 中ViewMetadata接口定义了uuid()、formatVersion()、location()、schemas()、currentVersionId()、versions()、history()、properties()等访问方法并声明了SUPPORTED_VIEW_FORMAT_VERSION 1与DEFAULT_VIEW_FORMAT_VERSION 1——这与规范中格式版本必须为 1的约束一致。同时check()方法会对formatVersion进行校验必须满足0 formatVersion SUPPORTED_VIEW_FORMAT_VERSION否则抛出IllegalArgumentException。JSON 的序列化/反序列化由 ViewMetadataParser.java 完成其中明确定义了全部 JSON 键view-uuid、format-version、location、current-version-id、versions、version-log、properties、schemas。注意序列化时properties仅在非空时写出schemas与versions、version-log均以数组形式写出。该解析器还支持读写 GZIP 压缩默认gzip详见后文属性部分。视图属性properties字段可以承载两类内容元数据如comment与影响视图维护行为的设置。仓库的 ViewProperties.java 集中定义了这些属性的常量与默认值配合 docs/docs/view-configuration.md 可得到完整参数表属性默认值描述write.metadata.compression-codecgzip元数据压缩编解码器none或gzipwrite.metadata.path视图位置 /metadata视图元数据文件的基础位置设置后元数据文件直接写入该路径下不再追加/metadataversion.history.num-entries10控制要保留的versions数量replace.drop-dialect.allowedfalse控制在 replace 操作期间是否允许丢弃某种 SQL 方言视图行为属性提交重试相关属性默认值描述commit.retry.num-retries4提交失败前的重试次数commit.retry.min-wait-ms100重试提交前的最小等待时间毫秒commit.retry.max-wait-ms600001 分钟重试提交前的最大等待时间毫秒commit.retry.total-timeout-ms180000030 分钟一次提交的总重试超时时间毫秒这些属性可以在创建/替换视图CREATE/REPLACE VIEW时设置也可以通过更新属性updateProperties的 API 设置对应源码注释 View properties that can be set during CREATE/REPLACE view or using updateProperties API。版本Versionsversions列表中的每个版本是一个结构体包含以下字段要求字段名描述requiredversion-id版本的 IDrequiredschema-id视图版本对应 schema 的 IDrequiredtimestamp-ms版本创建的时间戳距 epoch 的毫秒数requiredsummary关于版本的摘要元数据字符串映射requiredrepresentations视图定义的表示列表optionaldefault-catalog当 SELECT 中的引用不包含 catalog 时使用的 catalog 名requireddefault-namespace当 SELECT 中的引用是单个标识符时使用的命名空间当default-catalog为null或未设置时必须使用存储该视图的 catalog 作为默认 catalog。源码实现ViewVersionParser.java 完整实现了版本的 JSON 读写其字段常量与规范一一对应version-id、timestamp-ms、schema-id、summary、representations、default-catalog、default-namespace。值得注意的实现细节default-catalog仅在非 null 时才写出if (version.defaultCatalog() ! null)default-namespace通过Namespace的多级 levels 数组写出——这就是示例 JSON 中default-namespace: [ default ]采用数组形式的原因summary使用字符串映射JsonUtil.writeStringMap。另外从 ViewMetadata.java 可以看到ViewMetadata提供了按 ID 索引版本与 schema 的方法versionsById、schemasById并在访问当前版本/当前 schema 时校验其 ID 必须真实存在否则抛出IllegalArgumentException。摘要Summary摘要Summary是视图版本的字符串到字符串元数据映射。规范文档化的常见元数据键如下要求键值optionalengine-name创建该视图版本的引擎名称optionalengine-version创建该视图版本的引擎版本例如示例中summary: { engine-name: Spark, engine-version: 3.3.2 }表明该版本由 Spark 3.3.2 创建。表示Representations视图定义可以有多种表示方式。表示Representation是表达视图定义的规范化形式。关键规则一个视图版本可以拥有多个表示同一版本的所有表示必须表达相同的底层定义引擎可以自由选择使用哪一种视图版本是不可变的immutable。版本一旦创建就不能修改因此该版本的表示也不能更改。如果视图定义发生变化或需要新增表示必须创建新版本。每个表示至少包含一个公共字段type取值如下sql定义视图的 SQL SELECT 语句SQL 表示SQL 表示以 SQL SELECT 语句存储视图定义并携带 SQL 方言dialect等元数据。一个视图版本可以包含不同方言的多个 SQL 表示但每种方言最多一个 SQL 表示。要求字段名类型描述requiredtypestring必须为sqlrequiredsqlstringSQL SELECT 语句requireddialectstringsqlSELECT 语句的方言如trino或spark例如USE prod.defaultCREATE OR REPLACE VIEW event_agg ( event_count COMMENT Count of events, event_date) AS SELECT COUNT(1), CAST(event_ts AS DATE) FROM events GROUP BY 2上述创建语句会产生如下的sql表示元数据字段名值typesqlsqlSELECT\n COUNT(1), CAST(event_ts AS DATE)\nFROM events\nGROUP BY 2dialectspark如果创建语句在AS之前没有包含列名或注释则这些字段应被省略。示例中event_count带Count of events注释与event_date字段别名必须是视图版本schema的一部分。源码实现SQL 表示的实现集中在 BaseSQLViewRepresentation.java 与 SQLViewRepresentationParser.javaSQLViewRepresentationParser定义了sql与dialect两个 JSON 键序列化时依次写出type由公共ViewRepresentationParser.TYPE提供、sql、dialect三个字段反序列化时要求节点必须是 JSON 对象否则抛出IllegalArgumentException此外仓库中还存在 UnknownViewRepresentation.java用于处理未来可能出现的未知表示类型体现了格式的前向兼容设计。版本日志Version log版本日志追踪视图当前版本的变更历史。它是视图的历史记录可以用于重建在某个时间点视图所对应的版本。需要注意版本日志记录的不是版本的创建时间创建时间存储在各版本自身的元数据中。一个版本可以在版本日志中出现多次表示视图定义被回滚过。version-log中的每个条目是一个结构体要求字段名描述requiredtimestamp-ms视图current-version-id被更新的时间戳距 epoch 的毫秒数requiredversion-idcurrent-version-id被设置为的 ID源码实现ViewHistoryEntryParser.java 实现了版本日志条目的 JSON 读写仅包含version-id与timestamp-ms两个字段与规范完全一致。在 ViewMetadata.java 的setCurrentVersionId中可以看到版本日志的生成逻辑每次切换当前版本时都会构造一个ViewHistoryEntry如果该版本是在本次变更中新增的则使用该版本自身的时间戳否则使用当前系统时间System.currentTimeMillis()——这正好处理了视图定义在历史上曾被回滚、如今再次被激活的场景。附录 A完整示例以下通过一个完整示例说明 JSON 元数据文件格式。假设发生如下操作序列USE prod.defaultCREATE OR REPLACE VIEW event_agg ( event_count COMMENT Count of events, event_date) COMMENT Daily event counts AS SELECT COUNT(1), CAST(event_ts AS DATE) FROM events GROUP BY 2生成的元数据 JSON 文件如下。注意其路径有意与 Iceberg 表的路径相似使用metadata目录s3://bucket/warehouse/default.db/event_agg/metadata/00001-(uuid).metadata.json{ view-uuid: fa6506c3-7681-40c8-86dc-e36561f83385, format-version : 1, location : s3://bucket/warehouse/default.db/event_agg, current-version-id : 1, properties : { comment : Daily event counts }, versions : [ { version-id : 1, timestamp-ms : 1573518431292, schema-id : 1, default-catalog : prod, default-namespace : [ default ], summary : { engine-name : Spark, engine-version : 3.3.2 }, representations : [ { type : sql, sql : SELECT\n COUNT(1), CAST(event_ts AS DATE)\nFROM events\nGROUP BY 2, dialect : spark } ] } ], schemas: [ { schema-id: 1, type : struct, fields : [ { id : 1, name : event_count, required : false, type : int, doc : Count of events }, { id : 2, name : event_date, required : false, type : date } ] } ], version-log : [ { timestamp-ms : 1573518431292, version-id : 1 } ] }视图更新产生新元数据文件每一次变更都会产生一个新的元数据 JSON 文件。在下面的示例中底层 SQL 被修改为使用完全限定的表名USE prod.other_db; CREATE OR REPLACE VIEW default.event_agg ( event_count COMMENT Count of events, event_date) COMMENT Daily event counts AS SELECT COUNT(1), CAST(event_ts AS DATE) FROM prod.default.events GROUP BY 2更新视图会产生一个完全替换旧文件的新元数据文件s3://bucket/warehouse/default.db/event_agg/metadata/00002-(uuid).metadata.json{ view-uuid: fa6506c3-7681-40c8-86dc-e36561f83385, format-version : 1, location : s3://bucket/warehouse/default.db/event_agg, current-version-id : 2, properties : { comment : Daily event counts }, versions : [ { version-id : 1, timestamp-ms : 1573518431292, schema-id : 1, default-catalog : prod, default-namespace : [ default ], summary : { engine-name : Spark, engine-version : 3.3.2 }, representations : [ { type : sql, sql : SELECT\n COUNT(1), CAST(event_ts AS DATE)\nFROM events\nGROUP BY 2, dialect : spark } ] }, { version-id : 2, timestamp-ms : 1573518981593, schema-id : 1, default-catalog : prod, default-namespace : [ default ], summary : { engine-name : Spark, engine-version : 3.3.2 }, representations : [ { type : sql, sql : SELECT\n COUNT(1), CAST(event_ts AS DATE)\nFROM prod.default.events\nGROUP BY 2, dialect : spark } ] } ], schemas: [ { schema-id: 1, type : struct, fields : [ { id : 1, name : event_count, required : false, type : int, doc : Count of events }, { id : 2, name : event_date, required : false, type : date } ] } ], version-log : [ { timestamp-ms : 1573518431292, version-id : 1 }, { timestamp-ms : 1573518981593, version-id : 2 } ] }对比两份元数据文件可以清晰看到规范设计的精髓view-uuid保持不变——它标识视图本身与版本无关current-version-id从 1 变为 2——当前指针指向最新版本versions数组包含两个版本——元数据文件是自足的完整保留了 v1 与 v2因此可以把视图回滚到 v1version-log追加了新条目——记录了current-version-id的每一次切换含时间戳schemas保持 v1 的 schema——本次修改未改变列定义schema-id仍为 1说明 schema 是按 ID 复用而非重复存储的。测试验证与实现一致性仓库的单元测试对规范行为进行了覆盖验证例如 TestViewMetadata.java 中验证了version.history.num-entries必须为正数version.history.num-entries must be positive but was 0与 ViewProperties.java 中该属性默认值为10的实现保持一致。从源码结构还可以推断ViewMetadata通过MetadataUpdate如AddViewVersion、SetCurrentViewVersion、SetLocation、UpgradeFormatVersion来追踪一次变更中累积的修改再统一落盘为新元数据文件——这正是所有变更原子地写入新文件并交换指针这一设计在代码层面的具体实现。总结Apache Iceberg 视图规范为视图定义了与表格式并列的通用元数据格式核心要点可概括为自足的历史文件每个视图元数据文件包含完整版本历史支持回滚原子交换提交通过 metastore 原子地切换元数据指针读者无感知不可变版本任何定义变化都产生新版本版本一经创建不可修改多表示支持同一版本可同时携带多种方言的 SQL 表示供不同引擎选择版本日志记录current-version-id的每次切换可重建任意时间点的视图状态。该规范已完整落地于 core/src/main/java/org/apache/iceberg/view/ 的 Java 实现中配合 docs/docs/view-configuration.md 中的属性配置Iceberg 视图真正实现了一次创建、跨引擎共享、可版本回滚的目标。赞分享数据湖大数据数据存储【免费下载链接】icebergApache Iceberg项目地址https://gitcode.com/gh_mirrors/icebe/iceberg点击查看免费下载相关推荐Apache Iceberg 元数据管理终极指南深入解析表格式规范与最佳实践Apache Iceberg 元数据管理终极指南深入解析表格式规范与最佳实践 Apache Iceberg 是一款开源的大数据存储库专为处理大量时间序列数据数据湖大数据数据存储Backstage v1.12.0 版本解析Catalog 游标分页、Scaffolder Zod 动作定义与后端系统导出重命名Backstage v1.12.0 版本解析Catalog 游标分页、Scaffolder Zod 动作定义与后端系统导出重命名 本篇技术指南围绕 Backs数据湖大数据数据存储Egg.js 视图模板渲染egg-view统一视图引擎架构与实战指南Egg.js 视图模板渲染egg view统一视图引擎架构与实战指南 在 Egg.js 应用中绝大多数场景都需要“先取数据、再渲染模板”因此必须引入对后端Web框架上一篇如何解决矩阵路径问题从左上到右下的完整指南下一篇终极指南NSwag与NJsonSchema深度整合的JSON Schema处理最佳实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考