订阅更新

表结构变了,历史数据怎么办?——Schema演化和向前兼容怎么实现?

数据湖手册 第 12 题 约 6 分钟读完

本文目录
  1. 先说结论
  2. 真实案例:新增列导致查询报错
  3. Schema演化的类型
  4. 向前兼容vs向后兼容
  5. Delta Lake vs Iceberg vs Hudi对比
  6. 安全的Schema变更流程
  7. 面试高频追问
  8. 实战踩坑:Schema变更导致查询失败
  9. 总结

先说结论

Schema演化是什么:在不重写数据的情况下,修改表的Schema如增加列删除列修改列类型。数据湖支持Schema演化,传统Hive不支持.

向前兼容:旧Schema可以读新数据。如旧代码只有3列读新数据有5列,新增的2列会被忽略.

向后兼容:新Schema可以读旧数据。如新代码有5列读旧数据只有3列,缺失的2列会用NULL填充.

最佳实践:新增列用可选类型nullable、修改列名先增加新列再删除旧列、修改类型要向后兼容、生产环境变更前测试.

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

这道题面试官问得很多,因为Schema演化是数据湖的核心能力之一。我在某出海硬件品牌遇到过新增一列后,历史数据查询报错的问题。很多人只知道可以加列,但说不清楚向前兼容和向后兼容的区别。面试官想听的是你能说清楚原理和实战经验.

真实案例:新增列导致查询报错

2022年在某知名出海硬件品牌,我们的用户画像表user_profile原本有10列。业务部门要求新增一列phone_number手机号.

DBA直接用ALTER TABLE加了列,但新增的列默认是NOT NULL,历史数据没有phone_number,查询时报错.

-- 问题代码:新增列时没有指定NULLABLE
ALTER TABLE user_profile ADD COLUMN phone_number STRING;
 
-- 历史数据没有phone_number列,读取时报错

解决方案:我们重新新增列,显式指定NULLABLE.

-- 正确做法:新增列时显式指定NULLABLE
ALTER TABLE user_profile ADD COLUMN phone_number STRING NULL;

Schema演化的类型

新增列ADD COLUMN:最安全的操作,通常是向前兼容和向后兼容的。新增列对历史数据自动填充NULL。但要注意:新增列必须是nullable或有默认值.

删除列DROP COLUMN:危险操作!旧代码如果还依赖被删除的列,会报错。删除列前要确认:没有代码依赖这个列、已通知所有下游、备份了数据.

重命名列RENAME COLUMN:危险操作!旧代码如果用旧列名查询,会报错。重命名前要确认:没有代码依赖旧列名、已更新所有查询.

修改列类型CHANGE COLUMN TYPE:受限操作。只能拓宽类型如INT转BIGINT,不能收窄类型或不兼容转换.

向前兼容vs向后兼容

兼容类型定义示例如何实现
向前兼容旧代码可以读新数据旧代码3列读新数据5列新增列必须是nullable
向后兼容新代码可以读旧数据新代码5列读旧数据3列新增列必须有默认值或nullable
完全兼容既是向前兼容又是向后兼容新旧代码可以互读写新增nullable列加默认值

Delta Lake vs Iceberg vs Hudi对比

操作Delta LakeApache IcebergApache Hudi
新增列支持自动nullable支持需指定nullable支持
删除列支持元数据删除支持元数据删除支持
重命名列支持3.0加支持需要Schema evolution不支持
修改列类型受限拓宽类型受限拓宽类型受限

安全的Schema变更流程

步骤1:评估影响检查哪些代码查询报表依赖这个表.

步骤2:测试环境验证先在测试环境做Schema变更,用历史数据测试查询是否正常.

步骤3:选择变更方式优先用ADD COLUMN最安全,避免DROP COLUMN和RENAME COLUMN.

步骤4:灰度发布先对部分分区做Schema变更,验证无误后再全量表变更.

步骤5:监控和回滚变更后监控查询是否报错,如果发现问题立即回滚.

面试高频追问

追问一:向前兼容和向后兼容有什么区别?

向前兼容:旧代码可以读新数据,新增的列会被忽略。向后兼容:新代码可以读旧数据,缺失的列会用NULL填充。完全兼容需要:新增列是nullable或有默认值.

追问二:什么情况下Schema变更会失败?

四种情况会失败:新增列是NOT NULL且没有默认值;删除列但旧代码还在用这个列;重命名列但下游代码不知道;修改列类型收窄或不兼容.

追问三:如何安全地删除一列?

用两步法:先增加新列new_column,新代码改用新列;等旧代码下线后,再删除旧列old_column。这样避免了旧代码突然找不到列的问题.

实战踩坑:Schema变更导致查询失败

2023年在某出海硬件品牌,我们遇到过一次Schema变更导致查询失败的问题.

-- 问题场景:修改列类型导致数据转换失败
ALTER TABLE user_profile ALTER COLUMN user_id BIGINT;
 
-- 问题:历史数据中user_id有非数字的值
-- 查询时报错:Cannot convert abc123 to BIGINT

排查发现:user_id列有脏数据,包含非数字字符。ALTER COLUMN触发了全表重写,数据转换失败导致部分数据丢失.

解决方案:我们做了三件事:1)从备份恢复旧表;2)先清洗脏数据;3)新增一列user_id_bigint而不是修改原列的类型.

-- 正确做法:新增列而不是修改列类型
ALTER TABLE user_profile ADD COLUMN user_id_bigint BIGINT NULL;
UPDATE user_profile SET user_id_bigint = CAST(user_id AS BIGINT)
  WHERE user_id RLIKE '^[0-9]+$';

改进效果:改后查询正常,数据没有丢失.

总结

Schema演化是数据湖的核心能力,可以在不重写数据的情况下修改表结构。向前兼容是旧代码读新数据,向后兼容是新代码读旧数据。安全的Schema变更是:新增nullable列、避免删除列重命名列修改类型、生产环境变更前测试。面试时能说清楚向前兼容和向后兼容的区别、安全的Schema变更流程,就很稳.

下一篇聊一个很多人关心的问题:误删了数据,能恢复到昨天吗?——时间旅行Time Travel怎么用?

你们生产环境变更过Schema吗?遇到过什么问题?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器