砍了个木头2026
121.0MB · 2026-03-04
ALTER TABLE?在 MySQL(尤其是 InnoDB 引擎)中,不是所有 ALTER TABLE 操作都能“在线”完成。很多操作会触发 “表重建”(Table Rebuild),即:
创建一张新结构的临时表
逐行拷贝原表所有数据到新表
删除原表
将临时表重命名为原表名
这个过程在早期 MySQL 中是 完全锁表的(Copy Table),即使后来有了 Online DDL,仍有大量操作不支持“无锁”。
几乎所有 DDL 都会锁表(包括改字段名、改长度、加索引等)
拷贝期间:
写操作完全阻塞
读操作可能被阻塞(取决于存储引擎)
大表 = 长时间不可用
引入了 In-Place DDL,部分操作可在线进行(如扩展 VARCHAR 长度、添加索引)
但以下操作仍会重建表并锁写:
修改字段名(CHANGE COLUMN)
缩小字段长度(如 VARCHAR(100) → VARCHAR(50))
改变数据类型(如 INT → BIGINT,CHAR → VARCHAR)
修改字符集或排序规则
-- 显式要求 Online DDL,utf8mb4 字符集63是分界线要注意,不可用的时候有提示不影响数据库使用,建议使用pt-osc
ALTER TABLE users
MODIFY COLUMN email VARCHAR(63) NOT NULL,
ALGORITHM = INPLACE,
LOCK = NONE;
引入 Instant DDL,以下操作毫秒级完成,不触碰数据:
添加/删除列(末尾)
重命名列(RENAME COLUMN)
修改列默认值
但以下操作仍需重建表:
改变数据类型(如 INT → VARCHAR)
缩小字段长度
在非末尾位置加列
pt-online-schema-change 原理创建影子表 _原表名_new,并应用新结构(含新字段名/类型/长度)
分批拷贝历史数据(按主键分块,避免高 I/O)
通过触发器同步增量变更(INSERT/UPDATE/DELETE 自动同步到新表)
原子切换:RENAME TABLE 瞬间完成,业务无感
自动清理中间对象
-- 检查 MySQL 版本
SELECT VERSION();
-- 检查表结构和主键
SHOW CREATE TABLE your_table;
mysqldump -h your_host -u backup_user -p
--single-transaction your_db your_table > backup_$(date +%Y%m%d).sql
www.percona.com/downloads
# Mac
brew install percona-toolkit
| 原字段 | 目标字段 | ALTER 语句 |
|---|---|---|
type VARCHAR(50) | resource_type VARCHAR(100) NOT NULL | CHANGE COLUMN type resource_type VARCHAR(100) NOT NULL |
status TINYINT | order_status INT DEFAULT 0 | CHANGE COLUMN status order_status INT DEFAULT 0 |
name CHAR(20) | full_name VARCHAR(50) NOT NULL | CHANGE COLUMN name full_name VARCHAR(50) NOT NULL |
CHANGE COLUMN 同时支持改名 + 改类型
必须写出完整的新定义(类型、长度、是否为空、默认值)
pt-online-schema-change
--host=your_db_host
--user=dba_user
--password='your_password'
--alter "CHANGE COLUMN old_col new_col VARCHAR(100) NOT NULL DEFAULT ''"
D=your_db,t=your_table
--execute
--max-load="Threads_running=25"
--critical-load="Threads_running=50"
--chunk-size=1000
--sleep=0.1
--progress=time,30
--max-load:负载高时暂停(防雪崩)
--chunk-size:每次拷贝 1000 行(根据 I/O 调整)
--sleep:每批间隔 0.1 秒(降低压力)
--progress:每 30 秒输出进度
--alter 参数只需要写 ALTER TABLE 之后的部分,不能包含 ALTER TABLE 表名!
-- 检查结构
SHOW CREATE TABLE your_table;
-- 抽样查询(验证数据和默认值)
SELECT id, new_col FROM your_table WHERE id IN (1, 100000, 1000000);
-- 检查空值(如改为 NOT NULL)
SELECT COUNT(*) FROM your_table WHERE new_col IS NULL;