一、三大算法核心对比概览
1.1 算法对比总览表
| 特性维度 | INSTANT 算法 | INPLACE 算法 | COPY 算法 |
|---|
| 实现原理 | 仅修改元数据 | 原地修改数据文件 | 创建新表复制数据 |
| 阻塞级别 | 瞬间完成,基本不阻塞 | 轻度阻塞(短暂 MDL 锁) | 重度阻塞(长 MDL 锁) |
| 磁盘空间 | 几乎不占用额外空间 | 需要临时空间(索引重建) | 需要双倍空间 |
| 执行速度 | 亚秒级 | 秒到分钟级 | 分钟到小时级 |
| 适用版本 | MySQL 8.0.12+ | MySQL 5.6+ | 所有版本 |
| 回滚能力 | 支持(快速回滚) | 支持(中间状态) | 不支持 |
二、INSTANT 算法详解(最快速)
2.1 原理与实现机制
ALTER TABLE users ADD COLUMN last_login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ALGORITHM=INSTANT;
核心原理:
class InstantAlgorithm:
""" INSTANT 算法:仅修改数据字典(元数据),不修改实际数据文件 """
def execute(self, table, operation):
""" 执行流程:
1. 获取 MDL 锁(极短暂)
2. 在表数据字典中添加新列的元数据
3. 更新数据字典中的列默认值
4. 释放 MDL 锁
"""
metadata_change_only = True
alter_metadata({
'table_name': table.name,
'operation': 'ADD_COLUMN',
'column': {
'name': 'last_login_time',
'type': 'TIMESTAMP',
'default': 'CURRENT_TIMESTAMP',
'position': 'LAST'
}
})
return "Operation completed instantly"
2.2 支持的操作类型
ALTER TABLE t1 ADD COLUMN c INT, ALGORITHM=INSTANT;
ALTER TABLE t1 ADD COLUMN c INT AFTER b, ALGORITHM=INSTANT;
ALTER TABLE t1 DROP COLUMN c, ALGORITHM=INSTANT;
ALTER TABLE t1 RENAME COLUMN a TO b, ALGORITHM=INSTANT;
ALTER TABLE t1 ALTER COLUMN c SET DEFAULT 100, ALGORITHM=INSTANT;
ALTER TABLE t1 ALTER COLUMN c DROP DEFAULT, ALGORITHM=INSTANT;
ALTER TABLE t1 ALTER INDEX idx_name INVISIBLE, ALGORITHM=INSTANT;
ALTER TABLE t1 ALTER INDEX idx_name VISIBLE, ALGORITHM=INSTANT;
ALTER TABLE t1 MODIFY COLUMN status ENUM('active','inactive','pending'), ALGORITHM=INSTANT;
2.3 技术限制与内部实现
class InstantAlgorithmLimitations:
""" INSTANT 算法限制分析 """
def check_limitations(self, operation):
limitations = {
'row_format_requirement': '表必须是 DYNAMIC 或 COMPRESSED 格式',
'data_directory_requirement': '不能有 DATA DIRECTORY 子句',
'temporary_table': '不能是临时表',
'fulltext_index': '不能有 FULLTEXT 索引',
'spatial_index': '不能有 SPATIAL 索引',
'compressed_table': '不能是 COMPRESSED 格式',
'encrypted_table': '不能是加密表',
'max_instant_cols': 64,
'partition_table': '不支持分区表',
'virtual_columns': '不支持虚拟列操作'
}
return limitations
def internal_implementation(self):
""" 内部实现机制:
1. 在表结构末尾添加 instant 列位图
2. 新列数据存储在现有行的末尾
3. 对于已有数据,新列为 NULL 或默认值
4. 新插入数据包含新列值
"""
return {
'instant_bitmap': '在表空间头存储哪些列是 instant 添加的',
'column_count': '原始列数和 instant 列数分开存储',
'default_values': '通过数据字典存储默认值',
'space_management': '使用现有的未使用空间存储新列'
}
三、INPLACE 算法详解(最常用)
3.1 原理与执行过程
ALTER TABLE orders ADD INDEX idx_customer_id (customer_id), ALGORITHM=INPLACE, LOCK=NONE;
核心原理:
class InplaceAlgorithm:
""" INPLACE 算法:原地修改数据文件,不需要创建新表 """
def execute(self, table, operation):
""" 三阶段执行流程:
阶段 1: Prepare(准备阶段)
- 创建临时文件(.tmp)
- 准备需要的资源
- 获取共享 MDL 锁
阶段 2: Execute(执行阶段)
- 在原表空间内修改数据
- 重建索引(如需要)
- 更新数据字典
阶段 3: Commit(提交阶段)
- 提交修改
- 释放锁
- 清理临时文件
"""
phases = {
'phase1_prepare': {
'duration': '短暂',
'lock': 'MDL_SHARED_UPGRADABLE',
'operations': [
'验证操作可行性',
'分配临时空间',
'创建临时文件',
'初始化排序缓冲区'
]
},
'phase2_execute': {
'duration': '主要耗时阶段',
'lock': 'MDL_EXCLUSIVE(短暂)',
'operations': {
'add_index': '读取数据 -> 排序 -> 构建 B+ 树',
'drop_index': '标记索引为删除 -> 空间回收',
'optimize_table': '碎片整理,页重组'
}
},
'phase3_commit': {
'duration': '短暂',
'lock': 'MDL_EXCLUSIVE(极短)',
'operations': [
'原子切换新结构',
'更新数据字典',
'清理临时文件',
]
}
}
phases
3.2 支持的操作类型
ALTER TABLE orders ADD INDEX idx_order_date (order_date), ALGORITHM=INPLACE;
ALTER TABLE orders DROP INDEX idx_order_date, ALGORITHM=INPLACE;
ALTER TABLE orders RENAME INDEX old_name TO new_name, ALGORITHM=INPLACE;
ALTER TABLE users MODIFY COLUMN age TINYINT UNSIGNED, ALGORITHM=INPLACE;
ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id), ALGORITHM=INPLACE;
ALTER TABLE orders DROP FOREIGN KEY fk_user, ALGORITHM=INPLACE;
ALTER TABLE users ROW_FORMAT=DYNAMIC, ALGORITHM=INPLACE;
ALTER TABLE users CHARACTER SET utf8mb4, ALGORITHM=INPLACE;
ALTER TABLE locations ADD SPATIAL INDEX idx_point (point), ALGORITHM=INPLACE;
ALTER TABLE users FORCE, ALGORITHM=INPLACE;
3.3 INPLACE 算法变体:原地重建
class InplaceRebuildAlgorithm:
""" 原地重建算法:需要重建整个表,但仍在原表空间 """
def analyze_operations(self):
return {
'requires_full_rebuild': [
'修改列的数据类型(如 INT -> BIGINT)',
'修改字符集(需要重新编码)',
'修改 ROW_FORMAT',
'删除主键',
'添加/删除自增列',
'OPTIMIZE TABLE(碎片整理)'
],
'partial_rebuild': [
'添加二级索引(仅重建索引)',
'删除二级索引(仅更新元数据)',
'修改索引类型',
'修改索引可见性'
],
'performance_characteristics': {
'disk_space': '需要额外空间存储中间数据',
'memory_usage': '使用 sort buffer 排序',
'io_pattern': '顺序读取,随机写入',
'parallelism': 'MySQL 8.0 支持并行 DDL'
}
}
def execute_optimize_table(self):
""" OPTIMIZE TABLE 的 INPLACE 实现
注意:实际上会创建临时表,但使用 INPLACE 算法
"""
steps = [
'1. 创建与原表结构相同的临时表',
'2. 逐行从原表读取数据,插入临时表(重建行)',
'3. 删除原表,重命名临时表',
'4. 重建索引'
]
return {
'actual_algorithm': 'COPY 但标记为 INPLACE',
'reason': '需要重建整个表来消除碎片',
'space_requirement':
}
四、COPY 算法详解(最传统)
4.1 原理与执行过程
ALTER TABLE employees ADD COLUMN middle_name VARCHAR(50), ALGORITHM=COPY;
核心原理:
class CopyAlgorithm:
""" COPY 算法:创建新表,复制数据,替换原表 """
def execute(self, table, operation):
""" 五阶段执行流程:
阶段 1: 创建临时表
- 根据新结构创建临时表(#sql-xxx)
- 在新表上创建所有索引
阶段 2: 复制数据
- 逐行从原表读取数据
- 应用转换(如类型转换)
- 写入临时表
阶段 3: 同步数据
- 在复制期间应用 DML 操作
- 使用触发器捕获变更
阶段 4: 切换表
- 获取排他锁
- 重命名表:原表 -> 备份表,临时表 -> 原表
阶段 5: 清理
- 删除备份表
- 删除触发器
- 释放锁
"""
resource_requirements = {
'disk_space': '原表大小 * 2(最坏情况)',
'memory': '取决于 buffer pool 配置',
'io_operations': '全表扫描 + 全表写入',
'lock_duration': '整个复制过程持有排他锁',
'dml_blocking': '完全阻塞读写'
}
return resource_requirements
4.2 COPY 算法的内部触发器机制
CREATE TRIGGER trg_employees_insert AFTER INSERT ON employees FOR EACH ROW BEGIN
INSERT INTO tmp_employees VALUES (NEW.*);
END;
CREATE TRIGGER trg_employees_update AFTER UPDATE ON employees FOR EACH ROW BEGIN
UPDATE tmp_employees SET ... WHERE id = OLD.id;
END;
CREATE TRIGGER trg_employees_delete AFTER DELETE ON employees FOR EACH ROW BEGIN
DELETE FROM tmp_employees WHERE id = OLD.id;
END;
class CopyAlgorithmTriggers:
""" COPY 算法的触发器同步机制 """
def explain_sync_mechanism(self):
return {
'trigger_creation': {
'timing': '数据复制开始前',
'types': ['INSERT', 'UPDATE', 'DELETE'],
'purpose': '捕获 DDL 执行期间的 DML 变更'
},
'data_sync_flow': {
'step1': '用户执行 DML 操作',
'step2': '触发器捕获变更',
'step3': '应用到临时表',
'step4': '继续复制剩余数据'
},
'performance_impact': {
'dml_overhead': '每个 DML 操作额外执行触发器逻辑',
'trigger_maintenance': '需要维护触发器元数据',
'locking_issues': '可能产生死锁'
}
}
五、三大算法对比深度分析
5.1 性能特征对比矩阵
class AlgorithmComparison:
""" 三大算法详细对比分析 """
def comparison_matrix(self):
return {
'algorithm_comparison': {
'INSTANT': {
'blocking_level': 'LOW(仅元数据锁)',
'disk_io': '几乎为零',
'dml_impact': '几乎无影响',
'disk_space': '可忽略',
'recovery': '秒级回滚',
'applicability': '有限操作',
'version_requirement': 'MySQL 8.0.12+'
},
'INPLACE': {
'blocking_level': 'MEDIUM(分阶段锁)',
'disk_io': '中度(取决于数据量)',
'dml_impact': '阶段 2 短暂阻塞',
'disk_space': '需要临时空间',
'recovery': '可中断,有中间状态',
'applicability': '大部分 DDL 操作',
'version_requirement': 'MySQL 5.6+'
},
'COPY': {
'blocking_level': 'HIGH(全程排他锁)',
'disk_io': '重度(全表读写)',
'dml_impact': '完全阻塞',
: ,
: ,
: ,
:
}
},
: {
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
}
}
}
5.2 锁机制深度分析
LOCK TABLES users WRITE;
class LockMechanismAnalysis:
""" 三大算法的锁机制分析 """
def analyze_lock_patterns(self):
return {
'MDL_lock_types': {
'MDL_SHARED': '允许并发读,阻止写',
'MDL_SHARED_UPGRADABLE': '允许并发读,准备升级',
'MDL_EXCLUSIVE': '完全独占,阻止所有操作'
},
'algorithm_lock_usage': {
'INSTANT': {
'lock_type': 'MDL_EXCLUSIVE(极短暂)',
'duration': '亚秒级',
'concurrent_dml': '几乎无阻塞',
'lock_contention': '极低'
},
'INPLACE': {
'phase1': {
'lock': 'MDL_SHARED_UPGRADABLE',
'duration': '准备时间',
'dml': '允许读写'
},
'phase2': {
'lock': 'MDL_EXCLUSIVE',
'duration': '执行时间(主耗时)',
'dml': '完全阻塞'
},
'phase3': {
'lock': 'MDL_EXCLUSIVE',
'duration': '提交时间',
:
}
},
: {
: ,
: ,
: ,
:
}
},
: {
: [
,
,
,
],
: [
,
,
,
]
}
}
六、生产环境最佳实践
6.1 算法选择决策树
class AlgorithmDecisionTree:
""" Online DDL 算法选择决策树 """
def decide_algorithm(self, operation, mysql_version, table_size):
""" 决策逻辑:
1. 检查 MySQL 版本是否支持 INSTANT
2. 检查操作是否支持 INSTANT
3. 检查 INPLACE 是否支持
4. 默认使用 COPY
"""
decision_tree = {
'step1': '检查 MySQL 版本 >= 8.0.12?',
'step2': '检查操作是否支持 INSTANT?',
'step3': '检查操作是否支持 INPLACE?',
'step4': '使用 COPY 算法',
'instant_criteria': {
'mysql_version': '>= 8.0.12',
'table_format': 'DYNAMIC 或 COMPRESSED',
'operation_type': [
'ADD_COLUMN(任意位置)',
'DROP_COLUMN(8.0.29+)',
'RENAME_COLUMN',
'SET/DROP DEFAULT',
'索引可见性修改'
]
},
'inplace_criteria': {
'mysql_version': '>= 5.6',
'operation_type': [
'ADD/DROP INDEX',
'ADD/DROP FOREIGN KEY',
'RENAME TABLE',
'修改表选项',
'OPTIMIZE TABLE'
],
'considerations': [
'表大小影响执行时间',
'需要临时磁盘空间',
'阶段 2 会短暂阻塞'
]
},
: {
: [
,
,
,
],
: [
,
,
]
}
}
decision_tree
6.2 安全执行 Online DDL
ALTER TABLE users ADD COLUMN last_ip VARCHAR(45), ALGORITHM=INPLACE, LOCK=NONE;
SHOW WARNINGS;
EXPLAIN ALTER TABLE test_users ADD COLUMN last_ip VARCHAR(45), ALGORITHM=INPLACE, LOCK=NONE;
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME LIKE '%stage/innodb/alter%';
SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED,
(WORK_COMPLETED/WORK_ESTIMATED)*100 as progress_pct
FROM performance_schema.events_stages_current
WHERE EVENT_NAME LIKE '%alter%';
ALTER TABLE big_table ADD COLUMN new_col INT DEFAULT 0, ALGORITHMINSTANT;
big_table new_col , ALGORITHM;
big_table new_col id ;
big_table new_col id ;
6.3 故障处理与回滚
class DDLFailureRecovery:
""" Online DDL 故障处理策略 """
def handle_failure_scenarios(self):
scenarios = {
'instant_algorithm_failure': {
'cause': '不满足 INSTANT 条件但指定了 ALGORITHM=INSTANT',
'symptoms': '立即报错,无影响',
'recovery': '重新执行合适的算法',
'sql': "ALTER TABLE t1 ADD COLUMN c INT, ALGORITHM=INPLACE;"
},
'inplace_algorithm_failure': {
'cause': '磁盘空间不足、内存不足、锁超时',
'symptoms': 'DDL 中断,表处于中间状态',
'recovery_steps': [
'检查错误日志',
'查看 INFORMATION_SCHEMA.INNODB_TABLES',
'如果有临时表,手动清理',
'重启 MySQL(最后手段)'
],
'prevention': [
'确保足够磁盘空间',
'设置合理的 lock_wait_timeout',
'监控长事务'
]
},
'copy_algorithm_failure': {
'cause': '磁盘空间不足、进程被 kill、服务器重启',
'symptoms': '遗留#sql-xxx 临时表',
'recovery': {
'case1': '临时表存在,原表存在 → 手动清理临时表',
'case2': '临时表存在,原表丢失 → 重命名临时表',
'case3': '原表存在,临时表丢失 → 原表正常',
:
},
: [
,
]
},
: {
: ,
: ,
: [
,
,
,
],
: [
,
]
}
}
scenarios
七、高级优化技巧
7.1 MySQL 8.0 并行 DDL
SET SESSION innodb_ddl_threads = 4;
SET SESSION innodb_ddl_buffer_size = 1048576;
ALTER TABLE large_table ADD INDEX idx_column (column_name), ALGORITHM=INPLACE, LOCK=SHARED;
SELECT * FROM performance_schema.events_statements_current
WHERE SQL_TEXT LIKE '%ALTER TABLE%';
SHOW VARIABLES LIKE 'innodb_parallel%';
7.2 大表 DDL 优化策略
class LargeTableDDLOptimization:
""" 大表 Online DDL 优化策略 """
def optimization_strategies(self):
return {
'strategy_1': {
'name': '分阶段执行',
'description': '将一个大 DDL 拆分成多个小 DDL',
'example': [
'1. 添加允许 NULL 的列(INSTANT)',
'2. 分批更新默认值(避免长事务)',
'3. 修改列为 NOT NULL(INPLACE)'
],
'benefits': '减少单次锁持有时间'
},
'strategy_2': {
'name': '利用从库先行',
'description': '先在从库执行,验证后再在主库执行',
'steps': [
'1. 在从库执行 DDL',
'2. 监控复制延迟',
'3. 验证业务影响',
'4. 主库低峰期执行'
],
'tools': 'pt-online-schema-change, gh-ost'
},
'strategy_3': {
'name': '智能算法选择',
'description': '根据表大小和操作类型选择最优算法',
'decision_matrix': {
'small_table(<10GB)': '根据操作选择',
'medium_table(10-100GB)': '优先 INPLACE',
'large_table(>100GB)': '使用外部工具'
},
: [
,
,
]
},
: {
: ,
: ,
: {
: ,
: ,
: ,
:
},
: [
,
,
]
}
}
():
{
: ,
: {
: ,
: ,
: ,
: ,
:
},
: [
,
,
,
],
: [
,
,
,
]
}
八、面试要点总结
8.1 核心概念记忆点
- INSTANT 算法:元数据级别修改,亚秒级完成,MySQL 8.0+
- INPLACE 算法:原地修改,分阶段锁,支持大部分 DDL
- COPY 算法:创建新表复制数据,完全阻塞,兼容性好
关键区别:
- 阻塞程度:INSTANT < INPLACE < COPY
- 执行速度:INSTANT > INPLACE > COPY
- 磁盘使用:INSTANT < INPLACE < COPY
8.2 常见面试问题
Q1:什么情况下 INPLACE 算法实际上需要复制整个表?
答:INPLACE 算法在以下情况需要重建整个表:
- 修改列的数据类型(如 VARCHAR(10) → VARCHAR(20))
- 修改字符集(需要重新编码数据)
- 修改表的 ROW_FORMAT
- 删除主键
- 添加/删除自增列
- 某些分区表操作
虽然标记为 INPLACE,但实际上需要复制数据,只是不需要创建新表文件。
Q2:如何判断一个 DDL 操作使用哪种算法?
答:可以通过以下方式:
- 查看官方文档的 Online DDL 支持矩阵
- 使用 EXPLAIN 分析:EXPLAIN ALTER TABLE ...
- 查看 SHOW WARNINGS 输出
- 查询 INFORMATION_SCHEMA.INNODB_SYS_TABLES
- 测试环境实际执行观察
MySQL 8.0 开始,如果不指定 ALGORITHM,会按 INSTANT→INPLACE→COPY 顺序自动选择。
Q3:Online DDL 有哪些限制和风险?
答:主要限制和风险包括:
- 空间限制:需要足够的磁盘空间(尤其是 COPY 算法)
- 性能影响:INPLACE 的阶段 2 会短暂阻塞
- 复制延迟:在复制环境中可能导致延迟
- 外键约束:某些操作不支持有外键的表
- 触发器影响:COPY 算法使用触发器可能影响性能
- 元数据锁:可能与其他事务冲突
最佳实践:先在从库测试,使用监控工具,准备回滚方案。
Q4:MySQL 8.0 在 Online DDL 方面有哪些重要改进?
答:MySQL 8.0 的重要改进:
- INSTANT 算法:支持添加列等操作(8.0.12+)
- 原子 DDL:DDL 操作要么完全成功,要么完全回滚
- 并行 DDL:支持并行创建索引(8.0.14+)
- 更好的进度监控:通过 performance_schema
- DDL 日志:避免 DDL 执行中的数据丢失
- 更多的 INPLACE 操作:扩大了支持范围
这些改进大大提高了 DDL 的可靠性和性能。
这个完整的 Online DDL 三大算法解析,从基础原理到生产实践,涵盖了面试和工作中的各种场景,帮助你深入理解 MySQL 的 DDL 机制。