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就完事了。我个人习惯把它分成五个阶段。你看这张图:
每个阶段都有它的意义。我重点说说几个容易忽略的点:
需求评估阶段
别急着动手。先问自己三个问题:
- 这个字段真的需要吗?能不能用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变更,都是一次存储成本的博弈。后面几章,我们会深入每种变更类型,看看它们到底怎么影响你的账单。