表结构变了,历史数据怎么办?——Schema演化和向前兼容怎么实现?
本文目录
先说结论
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 Lake | Apache Iceberg | Apache 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年大数据老兵,聊技术、聊职场、聊成长