数据表新增字段

5 阅读9分钟

一、视频主题

这个视频主要讲的是一个 Java 后端面试高频场景:

面试官问:千万级订单表要新增字段,你敢直接执行 ALTER TABLE 吗?

示例 SQL:

ALTER TABLE orders  
ADD COLUMN source_channel VARCHAR(32) NOT NULL DEFAULT 'APP';

视频核心不是普通查询 SQL 调优,而是围绕 **大表 DDL 变更、在线改表、架构兜底设计、面试回答方式** 展开。

二、业务背景

视频假设的场景是:

电商系统
orders 表有 2000 万数据
现在需要给 orders 表新增字段 source_channel
字段类型:VARCHAR(32)
非空
默认值:APP

面试官追问:

能不能直接在线上执行 ALTER?
会不会锁表?
有没有更稳妥的方案?
如果历史数据要回填怎么办?

三、方案一:直接 ALTER —— 高风险方案

1. 直接执行

ALTER TABLE orders 
ADD COLUMN source_channel VARCHAR(32) NOT NULL DEFAULT 'APP';

2. 风险点

视频指出,大表直接 ALTER 风险很高,尤其是 MySQL 低版本或复杂 DDL 场景。

可能出现的问题:

1. 锁表
2. 业务写入阻塞
3. DDL 执行时间长
4. 磁盘 IO 飙升
5. 主从延迟
6. 线上查询/写入超时
7. 影响核心订单链路

3. MySQL 版本差异

视频里提到:

MySQL 5.5 之前:DDL 很容易锁全表
MySQL 5.6+:支持 Online DDL,但不是完全无风险

即使是 Online DDL,也可能在最后提交阶段产生短暂锁等待。


四、方案二:Online DDL —— 及格答案

MySQL 5.6+ 支持 Online DDL,执行过程大致分为:

初始化
  ↓
执行阶段
  ↓
提交阶段

1. 执行阶段

Online DDL 大部分时间不锁表,业务读写可以继续进行。

但是 DDL 过程中会记录增量变更日志。

相关参数:

innodb_online_alter_log_max_size

如果这个参数设置太小,在高并发写入场景下可能出现问题。

2. 风险点

Online DDL 不是完全无锁。

可能风险:

1. 最后提交阶段仍然可能等待 MDL 锁
2. 高并发场景下可能因为长事务导致 DDL 卡住
3. 大表 DDL 会造成 IO 压力
4. 可能引起主从延迟
5. 如果有慢查询或长事务,DDL 可能一直等待

3. 面试中不能只说 Online DDL

只回答:

用 Online DDL 就行

是不够的。

更好的回答应该补充:

1. 先看 MySQL 版本
2. 看字段变更类型
3. 看是否会重建表
4. 看当前业务高峰期和低峰期
5. 看是否存在长事务
6. 看主从延迟和磁盘空间
7. 准备回滚方案

五、方案三:pt-osc / gh-ost —— 高分方案

视频提到两类在线改表工具:

pt-online-schema-change,简称 pt-osc
gh-ost

1. pt-osc 思路

pt-osc 的核心思路是:

1. 创建影子表
2. 在影子表上执行表结构变更
3. 通过触发器同步源表增量数据
4. 拷贝历史数据到影子表
5. 最后 rename 切换表

缺点:

1. 需要触发器
2. 对数据库有额外压力
3. 最后 rename 阶段仍然可能有短暂锁
4. 老项目中使用较多

2. gh-ost 思路

gh-ost 的核心思路是:

1. 创建 ghost 表
2. 读取 binlog 同步增量数据
3. 拷贝历史数据
4. 最后切换表

优点:

1. 不依赖触发器
2. 通过 binlog 同步增量
3. 对业务表侵入更小
4. 可暂停、可限速、可观察
5. 更适合现代 MySQL 架构

3. gh-ost 示例命令

视频里展示了类似命令:

gh-ost \
  --database=mall \
  --table=orders \
  --alter="ADD COLUMN source_channel VARCHAR(32)" \
  --execute

实际生产中还需要补充:

1. 主库/从库连接配置
2. cut-over 策略
3. 限速参数
4. 延迟监控
5. chunk-size
6. max-load
7. critical-load
8. panic flag file

六、方案四:新建扩展表 —— 架构师思维

视频提出一个更偏架构设计的方案:不一定非要改原订单主表,可以新建订单扩展表。

示例:

CREATE TABLE order_ext (
    order_id BIGINT PRIMARY KEY,
    source_channel VARCHAR(32) NOT NULL DEFAULT 'APP'
);

1. 优点

1. 不改核心订单主表结构
2. 避免对大表直接 DDL
3. 降低核心链路风险
4. 扩展字段可以独立维护
5. 适合非核心字段、营销字段、渠道字段

2. 缺点

1. 查询订单详情时可能需要 JOIN
2. 高 QPS 场景下 JOIN 有性能成本
3. 需要考虑缓存或冗余
4. 需要保证扩展表数据一致性

3. 适合场景

适合这类字段:

渠道字段
营销字段
标签字段
非核心业务字段
变化频繁的扩展属性

不适合所有字段都放扩展表,否则查询复杂度会升高。


七、方案五:预留冗余字段 —— 防御性设计

视频提到可以提前预留一些扩展字段,例如:

ext_1
ext_2
ext_3
extra_json

1. 好处

1. 后续小需求不用频繁改表
2. 避免大表 DDL 风险
3. 对临时业务字段更友好

2. 风险

视频也强调,这种设计不能乱用。

常见问题:

1. 语义黑盒:ext_1 今天存渠道,明天可能又存活动 ID
2. 类型混乱:VARCHAR 里存数字、日期、JSON
3. 索引困难:想搜索时不好建索引
4. 维护困难:新人不知道 ext_3 代表什么
5. 容易变成垃圾字段

3. 正确使用方式

如果使用预留字段,必须配套:

1. 字段语义注册表
2. Wiki 文档
3. 字段使用约束
4. 字段负责人
5. 字段生命周期管理
6. 不允许随意复用

八、方案六:PostgreSQL 的降维打击

视频中还提到 PostgreSQL 在某些 DDL 场景下比 MySQL 更友好。

大意是:

PostgreSQL 11+ADD COLUMN DEFAULT 做了优化
某些加字段默认值操作不需要重写整张表

而 MySQL 面试中更多关注:

1. 怎么安全地改大表
2. 是否锁表
3. 是否产生主从延迟
4. 是否影响线上业务
5. 是否有回滚方案

这部分的核心意思是:

不同数据库对 DDL 的实现不同,不能把 MySQL 的经验直接套到 PostgreSQL 上。

九、视频给出的面试满分回答模板

视频最后总结了一个高分回答思路:

首先,我不会直接 ALTER。

我会先评估字段属性:
如果是核心高频查询字段,优先用 gh-ost 在线改表,低峰期执行,开启限速和监控。

如果是扩展属性,我会考虑新建扩展表,或者复用预留字段,避免动核心表结构。

同时准备回滚预案,确保磁盘空间和主从延迟可控。

这个回答体现了:

1. 风险意识
2. 工具熟练度
3. 架构前瞻性
4. 线上生产经验
5. 不盲目直接改表

十、面试官可能继续追问

视频结尾抛出了一个追问:

改完字段后,历史 2000 万条数据的 source_channel 怎么回填?

这个问题不能直接说:

UPDATE orders SET source_channel = 'APP';

因为一次性更新 2000 万数据会有很大风险。


十一、历史数据回填的推荐方案

1. 分批回填

推荐按主键范围分批更新:

UPDATE orders
SET source_channel = 'APP'
WHERE id > 0
  AND id <= 10000
  AND source_channel IS NULL;

然后循环推进:

1 ~ 10000
10001 ~ 20000
20001 ~ 30000
...

2. 控制每批大小

建议:

每批 1000 ~ 10000 

根据数据库压力动态调整。

3. 避免大事务

不要一次提交 2000 万条。

应该:

每批一个事务
更新后提交
短暂 sleep
继续下一批

4. 增加回填进度表

可以设计一张任务表:

CREATE TABLE data_backfill_task (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    table_name VARCHAR(64),
    field_name VARCHAR(64),
    last_id BIGINT,
    status VARCHAR(32),
    create_time DATETIME,
    update_time DATETIME
);

用于记录:

1. 当前回填到哪个 ID
2. 是否执行成功
3. 是否可以断点续跑
4. 失败后从哪里恢复

5. 低峰执行

建议在:

凌晨
业务低峰期
数据库压力低的时候

执行回填任务。

6. 监控指标

回填时必须监控:

1. CPU
2. 磁盘 IO
3. 慢 SQL
4. 主从延迟
5. 锁等待
6. QPS
7. 业务接口耗时

十二、完整面试回答示例

如果面试官问:

千万级订单表加字段,你敢直接 ALTER 吗?

可以这样回答:

我不会直接在线上执行 ALTER,尤其是 orders 这种核心大表。

我会先判断 MySQL 版本、字段类型、是否带默认值、是否 NOT NULL、是否会触发表重建,以及当前表的数据量、QPS、主从延迟和磁盘空间。

如果这个字段是核心查询字段,必须放在 orders 表里,我会优先使用 gh-ost 或 pt-osc 做在线变更,在业务低峰期执行,并配置限速、主从延迟监控、cut-over 策略和回滚方案。

如果这个字段只是扩展属性,比如渠道、营销、标签类字段,我会优先考虑新建 order_ext 扩展表,避免直接修改核心订单主表。

如果公司已有规范,也可以使用预留字段,但必须有字段语义注册表和 Wiki 约束,避免 ext 字段语义混乱。

对于历史数据回填,我不会一次性 UPDATE 全表,而是按主键范围分批回填,每批几千条,记录回填进度,支持失败重试和断点续跑,同时监控主从延迟、锁等待和业务接口耗时。

十三、视频核心结论

千万级订单表加字段,不能只会说直接 ALTER。

真正的高分答案应该包括:

1. 不直接裸 ALTER
2. 先评估字段属性和业务影响
3. MySQL 5.6+ 可以考虑 Online DDL,但仍有锁等待风险
4. 大表核心字段优先使用 gh-ost / pt-osc
5. 非核心扩展字段可以考虑扩展表
6. 临时扩展可以考虑预留字段,但必须有字段语义管理
7. 历史数据回填必须分批、限速、可恢复
8. 全过程要监控主从延迟、锁等待、磁盘和业务接口耗时

十四、一句话总结

这个视频讲的是:面对千万级订单表新增字段,不能简单直接 ALTER,而要根据字段重要性、数据库版本、业务压力和风险等级,选择 Online DDL、gh-ost/pt-osc、扩展表或预留字段,并配合低峰执行、限速、监控、回滚和分批回填。