湖屋——第6阶段:模式演进
内容提要
湖屋中的Parquet文件不可修改,模式演进通过元数据实现:每列创建时分配不可变字段ID并写入文件,读取时按ID映射,无需重写数据。新增列分配新ID,旧文件缺失该ID则填NULL;删除列仅设结束快照,保留历史以支持时间旅行;重命名列不改ID,旧文件自动适配。模式版本仅在结构变更时递增,以保证同版本文件可安全合并。
延伸解读
字段ID是模式演进的核心机制
文章反复强调,Parquet文件按字段ID而非列名匹配。新增列分配新ID,旧文件缺失该ID时读取端填NULL;重命名只改目录中的名称,ID不变,旧文件自动适配。这意味着模式变更的成本主要在元数据层,而非重写海量数据文件。理解这一点,就能明白为何湖仓能在不触碰历史文件的前提下支持加列、删列和改名。
删除列保留历史,时间旅行仍可访问
删除列并非物理删除,而是给目录行设置end_snapshot。当前查询通过可见性过滤不再请求该字段,但按旧快照查询时,只要end_snapshot大于目标快照,该列仍会被包含。因此删除操作对历史数据是可逆的,代价是Parquet文件中该列的数据继续存在,存储不会立即释放。读者应区分“逻辑删除”与“物理清理”。
schema_version只随结构变更递增
文章指出,插入等数据操作不改变schema_version,只有加列、删列、改名等结构变更才递增。该版本号用于压缩安全:同一schema_version下写入的文件可以安全合并,无需做列对账。这提示读者,模式版本是判断文件兼容性的轻量依据,而不是每次写入都变化的计数器。
Q&A
湖屋架构下为什么不能直接修改Parquet文件来改表结构?
Parquet文件是不可修改的密封单元,重写PB级文件只为加一列会耗时数小时,因此湖屋通过元数据实现模式演进,不触碰数据文件。
湖屋中字段ID(field ID)是什么,有什么作用?
字段ID是每列创建时分配的不可变整数标识,写入Parquet文件作为模式元数据,读取时用它把文件列映射到目录列,从而解耦文件模式与当前模式。
在湖屋表里新增一列时,旧数据文件会怎样处理?
新增列分配新字段ID并插入元数据行,旧文件保持不变;读取旧文件时,因文件缺少该字段ID,该列所有行自动填充NULL,无需重写数据。
湖屋中删除列后,历史数据还能通过时间旅行查到吗?
能。删除列只是设置end_snapshot,字段ID保留在历史中;查询旧快照时可见性过滤会包含该列,数据仍在Parquet文件中,只是新快照的扫描不再请求该字段ID。
重命名湖屋表的列需要重写已有的Parquet文件吗?
不需要。重命名只更新目录中的列名,字段ID不变;Parquet文件按字段ID引用列,因此所有旧文件自动适配新列名,读取结果集显示新名称。
湖屋的模式版本(schema_version)在什么情况下递增,有什么用途?
模式版本仅在结构变更(如增删列、重命名)时递增,数据操作不改变它;它用于压缩安全,保证同一模式版本下写入的文件可以安全合并而无需列协调。