1. Schema变更基础:从一次“小改动”说起

各位同学好。今天咱们聊聊Schema变更——这个听起来有点枯燥,但实际工作中坑最多的主题之一。

先讲个我自己的经历。几年前我负责一个电商系统的数据库,某天产品经理跑过来说:“就在订单表加个‘是否已读’字段,很小的改动。”我当时没多想,直接ALTER TABLE加了一列。结果呢?那晚数据库CPU飙到100%,主从延迟从毫秒级变成分钟级,线上订单差点崩了。嗯,从那以后,我对Schema变更再也不敢掉以轻心。

说白了,Schema变更就是修改数据库表的结构定义。你想想看,一个生产环境的大表,几亿行数据,你动一下它的结构,背后可能牵动存储引擎、索引重建、数据迁移、甚至整个集群的IO和网络。所以,理解Schema变更,是控制存储成本的第一步。

1.1 什么是Schema变更

Schema,就是数据库里表、字段、索引、约束这些“骨架”的定义。变更,就是改这个骨架。

举个例子:

-- 原始表结构
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id INT NOT NULL,
    amount DECIMAL(10,2),
    created_at DATETIME
);

-- 加一个字段
ALTER TABLE orders ADD COLUMN is_read TINYINT DEFAULT 0;

你看,加一个字段,就是一次Schema变更。但背后发生了什么?MySQL的InnoDB引擎,对于某些ALTER TABLE操作,会重建整张表。重建意味着:把原表数据全部拷贝到临时表,重建索引,最后再重命名。这个过程,IO开销巨大,临时表还会占用额外的存储空间。

核心观点:每一次Schema变更,都可能产生“隐性存储成本”。这些成本平时看不见,月底账单来了才心疼。

1.2 常见的Schema变更类型

我按实际工作中遇到的频率,把变更类型排了个序。你对照看看,是不是也这样?

变更类型 典型SQL 存储影响 我踩过的坑
新增列 ADD COLUMN 表重建,临时表占用额外空间 大表加列导致磁盘爆满
修改列类型 MODIFY COLUMN 通常全表重建,数据转换 INT改BIGINT,重建花了3小时
删除列 DROP COLUMN 表重建,但释放空间不立即 删了列,磁盘空间没降下来
新增索引 CREATE INDEX 索引文件增长,写入性能下降 加索引导致从库延迟
修改默认值 ALTER COLUMN ... SET DEFAULT 元数据变更,不重建表 这个相对安全,但也要注意
重命名列 CHANGE COLUMN 表重建 改名后应用报错,回滚更麻烦

这里我特别想强调一点:不是所有变更都重建表。比如MySQL 8.0的INSTANT ADD COLUMN,只改元数据,不重建。但大部分传统数据库,尤其是你还在用MySQL 5.7的话,加列就是重建。所以,版本很重要。

1.3 Schema变更的生命周期

一个完整的Schema变更,不是跑一条SQL就完事了。我个人习惯把它分成五个阶段。你看这张图:

Schema变更生命周期 需求评估 变更影响分析 方案设计 选工具/定策略 预发布验证 测试环境演练 灰度执行 分批/限流/监控 复盘 账单对比 各阶段关注点: • 需求评估:变更是否必要?有没有替代方案? • 方案设计:用原生DDL还是工具(pt-osc/gh-ost)?会不会锁表? • 预发布验证:在测试环境跑一遍,看执行时间和存储变化。 • 灰度执行:先对一个小表或分片执行,观察监控和账单。 • 复盘:对比变更前后的存储用量、IOPS、账单金额。

每个阶段都有它的意义。我重点说说几个容易忽略的点:

需求评估阶段

别急着动手。先问自己三个问题:

  • 这个字段真的需要吗?能不能用JSON字段或者扩展属性表代替?
  • 变更后,存储空间会增加多少?索引文件会膨胀多少?
  • 有没有低峰期可以执行?

我曾经遇到一个团队,为了一个“可能以后会用”的字段,给10亿行的表加了列。结果呢?字段从来没被用过,但表重建多花了500GB的临时空间,账单上多了一笔不小的开销。

方案设计阶段

选工具很关键。原生ALTER TABLE在MySQL里会锁表,对于大表,我建议用pt-online-schema-change或者gh-ost。它们通过触发器或binlog同步,做到在线变更,对业务影响小。

小技巧:用gh-ost变更时,可以设置--max-load参数,当数据库负载超过阈值时自动暂停。这样既能完成变更,又不会把数据库搞崩。

预发布验证

这一步很多人跳过。但我想说,测试环境跑一遍,能发现80%的问题。比如:

  • 变更SQL会不会锁等待?
  • 临时表需要多少额外空间?
  • 变更后索引是否被正确使用?

我记得有一次,在测试环境跑MODIFY COLUMN,发现执行计划变了,原来走索引的查询变成了全表扫描。还好提前发现了,不然上线就是事故。

灰度执行

别一把梭。先对一个小表或者一个分片执行,观察:

  • 变更耗时是否符合预期?
  • 磁盘IO有没有飙升?
  • 主从延迟有没有增加?
  • 存储账单有没有异常波动?

灰度没问题,再逐步扩大到全量。这个过程虽然慢,但安全。

复盘

变更完了,别忘了看账单。我习惯在变更前后各截一张存储监控图,对比一下:

  • 表空间大小变化
  • 索引空间大小变化
  • 临时表占用的空间是否已释放
  • 当月存储费用变化

这些数据,是下次做成本预估的重要参考。

警告:删除列不会立即释放磁盘空间。InnoDB的DROP COLUMN只是标记该列数据为“废弃”,实际空间要等表重建或OPTIMIZE TABLE后才释放。所以,删列不等于省钱,别被表象骗了。

小结

这一章我们聊了Schema变更的基础。说白了,就是:

  • 什么是Schema变更——改表结构
  • 常见类型——加列、改类型、删列、加索引
  • 生命周期——评估、设计、验证、执行、复盘

记住一句话:每一次Schema变更,都是一次存储成本的博弈。后面几章,我们会深入每种变更类型,看看它们到底怎么影响你的账单。