mysql 5.6前加索引会锁表阻塞dml;5.7+虽支持inplace但大字段或innodb_file_per_table=off时仍可能拷贝表,需结合版本、配置与explain预判,并优先用pt-online-schema-change处理大表。

线上加索引为什么会导致表锁或性能抖动
MySQL 5.6 以前,ALTER TABLE ... ADD INDEX 会重建整张表,期间阻塞 DML(INSERT/UPDATE/DELETE),主从延迟可能陡增。5.7+ 虽支持 ALGORITHM=INPLACE,但并非所有场景都真正“无锁”:比如对大字段(TEXT、BLOB)建索引,或在低版本 innodb_file_per_table=OFF 下,仍可能触发拷贝表行为。
实操建议:
- 先用
EXPLAIN FORMAT=JSON ALTER TABLE t ADD INDEX idx_a (a);(MySQL 8.0.19+)或SHOW CREATE TABLE t+ 查看innodb_file_per_table和 MySQL 版本,预判是否走原地算法 - 避开业务高峰,且确保
tmpdir有足够空间(INPLACE 过程中仍需临时排序文件) - 对千万级以上表,优先考虑
pt-online-schema-change(需确认从库无延迟、binlog_format=ROW)
哪些索引操作是“安全”的,哪些必须停机评估
不是所有索引变更风险等同。DROP INDEX 几乎总是轻量级(仅删元数据和 B+ 树节点),而 ADD INDEX、RENAME INDEX(MySQL 5.7+)、ALTER TABLE ... FORCE 都需谨慎。
关键判断点:
-
DROP INDEX:通常毫秒级,但需确认该索引未被FORCE INDEX或查询计划强依赖(可通过performance_schema.table_io_waits_summary_by_index_usage查使用频次) -
ADD INDEX:若列上有大量NULL值或存在长前缀(如VARCHAR(2000)),InnoDB 会为每行生成索引项,内存和 IO 开销陡增 -
RENAME INDEX:仅改字典名,安全;但若应用代码里硬编码了旧索引名(如某些 ORM 的 hint), rename 后可能失效
如何验证索引生效且不拖慢查询
加完索引不等于查询就变快——优化器可能因统计信息过期、索引选择性差或隐式类型转换而弃用它。
必须做的三件事:
- 执行
ANALYZE TABLE t更新统计信息(尤其在数据分布突变后) - 用
EXPLAIN SELECT ...确认key字段显示新索引名,且rows显著下降;注意type=ALL或Extra=Using filesort是否仍在 - 对比加索引前后慢查询日志:
long_query_time设为 0.1s,抓取相同条件的SELECT,看Query_time和Rows_examined变化
安全优化方案里最容易被忽略的细节
很多团队只关注“加什么索引”,却忽略索引背后的系统约束。比如:
-
innodb_buffer_pool_size不足时,新索引反而加剧 buffer pool 淘汰,导致更多磁盘随机读 - 复合索引顺序错误(如
INDEX(a,b,c)但高频查WHERE b=?),优化器无法跳过前导列,索引实际不可用 - 没清理冗余索引:
INDEX(a)和INDEX(a,b)共存时,前者几乎无价值,还拖慢写入
真正安全的调整,是把索引当“服务接口”来管:有监控(information_schema.STATISTICS + 慢日志)、有灰度(先在从库加、观察 24 小时)、有回滚预案(记录原始 SHOW CREATE TABLE 结果,而非依赖记忆)。










