本节摘要:DDL 负责创建和修改库表结构,它的特殊之处在于改动的是"容器"而不是"内容",很多操作会触碰整张表。本节讲 CREATE、ALTER、DROP 的工程用法,重点拆解 MySQL 8.0 在线 DDL 的能力边界与 ALGORITHM 参数。位置:SQL 家族的第一站,为后面所有读写提供结构前提。
增删改查的语句错了,改回来就行;DDL 错了,代价按分钟甚至小时计。评审会风险清单上,大表 DDL 永远排在前三:一张三千万行的订单表上执行一次需要重建的 ALTER,视机器配置可能锁写几分钟到几小时,主从延迟飙升,所有依赖这张表的业务跟着抖。理解 DDL 的正确姿势因此不是背语法,而是理解每条语句背后的执行代价。
MySQL 8.0 的 InnoDB 支持在线 DDL,核心是两个参数:ALGORITHM=INPLACE 尽量在引擎内部完成、不复制全表;ALGORITHM=COPY 则要新建一张影子表复制全部数据再换名。同为"加一列",8.0 的 ALGORITHM=INSTANT 只改元数据、瞬间完成;而"改列类型"几乎总是 COPY。能力边界记一张简表就够:
| 操作 | 典型算法 | 线上风险 |
|---|---|---|
| 加列(末尾) | INSTANT | 极低,秒级 |
| 加索引 | INPLACE | 低,可并发 DML |
| 改列类型 | COPY | 高,重建整表 |
| 缩短 VARCHAR 长度 | 视情况 | 中,需确认 |
| DROP COLUMN | INSTANT 或 INPLACE | 中低 |
| 改字符集 | COPY | 高,全表重写 |
背景:订单系统上线,走一遍标准 DDL 流程;三个月后需求要给 orders 加"渠道来源"字段。操作分两幕。
第一幕,建库建表(注意每个语句携带的工程信息):
CREATE DATABASE mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; CREATE TABLE orders ( order_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, customer_id BIGINT UNSIGNED NOT NULL, amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_customer (customer_id, created_at) ) ENGINE=InnoDB COMMENT '订单主表';
几个细节:字符集在建库时统一声明,表自动继承,避免"有的表 utf8 有的表 latin1"的漂移;idx_customer 是复合索引,先按客户查再看时间,覆盖"查某人最近订单"这个最高频访问模式——索引设计方法论第 4 章展开,这里先让结构带着正确索引上线。
第二幕,三个月后加字段:
ALTER TABLE orders ADD COLUMN channel TINYINT NOT NULL DEFAULT 0 COMMENT '来源渠道', ALGORITHM=INSTANT; -- 结果:Query OK, 0 rows affected(秒级完成)
结果与解读:8.0 的加列默认走 INSTANT,元数据修改即刻完成,存量行读该列时按默认值补齐,未写满的页延后物化。这是 8.0 相对 5.7 最实用的改进之一。变式一:如果是 5.7 环境,加列是 INPLACE,加一列到末尾通常仍可在线执行,但数据量大时 IO 压力不可忽视,应挂低峰期执行并监控主从延迟。变式二:若需求是"把 amount 从 DECIMAL(10,2) 改成 DECIMAL(12,2)",则必须评估 COPY 代价,标准做法是借助 gh-ost 或 pt-online-schema-change 这类在线改表工具,用触发器或 binlog 回放的方式慢慢搬迁,不阻塞业务。
DROP 连表带结构一起删;TRUNCATE 保留结构清空数据,且不走逐行删除、不触发触发器、通常不可回滚。评审会上对这两条命令的标准追问是:"在哪个环境执行?有没有备份?影响行数是全表吗?"生产惯例:高危语句一律先在测试环境演练,生产执行时用 WHERE 限定影响范围的 DELETE 替代无条件的 TRUNCATE,或者走工单审批双人复核。
RENAME TABLE,原子操作,毫秒级;先删后建中间的服务中断窗口是纯事故;ALGORITHM=INPLACE 试探,失败会报错而不会执行)确认代价。追问一:INSTANT 加列有限制吗? 有,且限制常被忽略。INSTANT 只支持在列末尾追加(或指定 AFTER 到某个位置,8.0.29 起才放开任意位置);列带非空约束时必须给默认值;同时做"加列又加索引"这类组合动作会整体降级到 INPLACE。实操建议:把 DDL 拆开执行——先走 INSTANT 的加列,再单独加索引,每步的风险档位都最优化。
追问二:DDL 会不会造成主从延迟? 会,而且是主从延迟的头号来源之一。COPY 类 DDL 在主库慢慢搬数据,binlog 里记录的是搬完的结果,从库重放同样要搬一遍;INPLACE 加索引期间产生的增量写入也在堆重放队列。所以大表 DDL 的监控清单里必须有从库延迟曲线,执行窗口选在延迟低谷期,必要时暂停非关键从库的重放避免读流量异常。
追问三:上线 DDL 的工单里必须写清什么? 五项缺一不可:目标表与当前行数、执行算法与预估时长、是否影响主从延迟、回滚方案(多数 DDL 的回滚是反向 DDL,同样要评估代价)、执行窗口与值班人。评审会上看到"待定"字样出现在任何一项,这张工单就退回。DDL 的事故几乎都出在信息不全的工单上,把信息写全本身就是一次风险自查。
要点回顾:DDL 改的是容器,代价按整表计;8.0 的 INSTANT/INPLACE/COPY 三档决定线上风险等级;大表改结构走在线工具或低峰窗口;RENAME 替代先删后建;DDL 工单五要素写全才算评审通过。结构就位,下一节往表里放数据、取数据。