作为数据库管理员,我们经常需要在生产环境中对表结构进行调整,其中最常见的操作之一就是添加索引。记得我第一次在千万级用户表上执行CREATE INDEX时,整个应用几乎停滞了半分钟,那种心惊肉跳的感觉至今难忘。后来才发现,原来MySQL早已提供了更优雅的解决方案——Online DDL技术。
在MySQL 5.6之前,任何DDL操作(包括添加索引)都会导致表锁,这意味着在索引创建期间,所有对该表的读写操作都会被阻塞。这对于7×24小时运行的生产系统简直是灾难性的。但自从Online DDL出现后,我们终于可以在不影响业务的情况下完成索引创建,这背后的技术原理值得每个DBA深入了解。
传统DDL操作采用"全表锁+数据复制"的工作模式。当执行ALTER TABLE添加索引时:
这种方式的弊端显而易见:在步骤1-4期间,表完全不可用。对于GB级别的大表,这个过程可能持续数小时。
而Online DDL则采用了更智能的"原地修改"策略:
在实际使用中,我们需要特别关注两个关键参数:
ALGORITHM选项:
LOCK选项:
一个生产环境推荐的最佳实践示例:
sql复制ALTER TABLE user_orders
ADD INDEX idx_order_time (order_time),
ALGORITHM=INPLACE,
LOCK=NONE;
Online DDL之所以能够不锁表,主要依赖于InnoDB的以下特性:
整个过程类似于git的分支管理——主分支(原表)继续接受提交(DML),同时在特性分支(新索引)上工作,最后通过快速合并完成切换。
虽然Online DDL很强大,但并非所有操作都支持:
完全支持的操作:
部分支持的操作:
完全不支持的操作:
对于超过1TB的超大表,即使使用Online DDL也可能对系统产生明显影响。以下是几个实测有效的优化技巧:
sql复制-- 先在测试环境验证
CREATE TABLE test_table LIKE production_table;
INSERT INTO test_table SELECT * FROM production_table LIMIT 100000;
ALTER TABLE test_table ADD INDEX idx_test(...);
sql复制SET GLOBAL innodb_online_alter_log_max_size=2147483648; -- 增大临时日志空间
SET GLOBAL innodb_sort_buffer_size=67108864; -- 增加排序缓冲区
选择低峰期执行:即使不锁表,IO和CPU压力仍会影响性能
监控进度:通过performance_schema可以查看进度
sql复制SELECT * FROM performance_schema.events_stages_current
WHERE EVENT_NAME LIKE '%alter%';
问题1:执行Online DDL时出现"ERROR 1799 (HY000)"错误
原因:临时日志空间不足
解决方案:增大innodb_online_alter_log_max_size参数
问题2:ALTER TABLE执行时间远超预期
可能原因:
- 系统自动选择了COPY算法
- 有未提交的长事务持有MDL锁
排查方法:
sql复制SHOW PROCESSLIST;
SELECT * FROM information_schema.innodb_trx;
问题3:添加索引后查询性能反而下降
典型原因:基数(cardinality)统计不准确
解决方案:
sql复制ANALYZE TABLE table_name;
特别值得注意的是,MySQL 8.0引入了"INSTANT"算法,对于某些操作如添加可为NULL的列,可以在常数时间内完成:
sql复制ALTER TABLE users ADD COLUMN last_login_time TIMESTAMP NULL, ALGORITHM=INSTANT;
为确保操作安全,建议遵循以下流程:
sql复制SELECT table_name,
round(((data_length + index_length) / 1024 / 1024), 2) as size_mb
FROM information_schema.TABLES
WHERE table_schema = 'your_db';
sql复制SELECT * FROM information_schema.innodb_ddl_log;
对于特别敏感的生产环境,还可以考虑以下替代方案:
使用gh-ost工具:
bash复制gh-ost \
--user="dba" \
--password="xxx" \
--host="mysql-host" \
--database="your_db" \
--table="your_table" \
--alter="ADD INDEX idx_name (name)" \
--execute
Percona的pt-online-schema-change:
bash复制pt-online-schema-change \
--alter "ADD INDEX idx_email (email)" \
D=your_db,t=your_table \
--execute
这些工具采用触发器机制实现真正的零锁表,但代价是会产生更多的临时空间和复制开销。
在多年的DBA工作中,我发现最稳妥的做法是:对于小型表直接使用Online DDL;对于中型表在低峰期执行;对于超大型表则优先考虑专业工具。同时,建立完善的变更管理流程同样重要——每次索引添加都应该有明确的性能目标,并通过EXPLAIN验证执行计划是否按预期使用新索引。