删除索引前设为invisible是唯一能闭环验证影响的操作,它不删数据、不重建、不改结构,仅让优化器不可见,支持真实负载下观察24小时再决策;主键禁止设为不可见,unique索引可设但唯一性校验仍生效;验证必须查information_schema.statistics而非show index;force index会绕过不可见性导致报错,需提前清理;备份恢复后状态可能失效,需立即核对。

删除索引前设为 INVISIBLE 是唯一能闭环验证影响的操作
直接 DROP INDEX 风险太高:千万级表重建索引可能卡写入数小时,慢查询、超时、主从延迟会立刻爆发。INVISIBLE 不删数据、不重建、不改结构,只让优化器“看不见”——你能在真实负载下观察 24 小时,看 Handler_read_next 是否飙升、慢日志是否新增、应用是否告警,再决定删还是留。
ALTER TABLE ALTER INDEX INVISIBLE 必须严格按语法执行
错一个词就失败,不是配置问题,是语法硬校验:
-
ALTER TABLE t1 ALTER INDEX idx_name INVISIBLE是唯一合法写法;写成SET INVISIBLE、MODIFY INDEX idx_name INVISIBLE或CHANGE INDEX全部报错ERROR 1064 (42000) - 主键(含显式
PRIMARY KEY和隐式主键,比如首个UNIQUE NOT NULL列)禁止设为不可见,执行直接失败:ERROR 3522 (HY000): Primary key cannot be invisible - 唯一约束索引(
UNIQUE KEY)可以设为不可见,但INSERT/UPDATE仍强制校验唯一性,失败风险不变 - 操作只更新
INFORMATION_SCHEMA.STATISTICS.IS_VISIBLE字段,毫秒完成,但长事务未提交时会被元数据锁(MDL)阻塞
别信 SHOW INDEX,必须查 INFORMATION_SCHEMA.STATISTICS
SHOW INDEX FROM t1 在 MySQL 8.0+ 虽有 Visible 列,但旧版客户端、ORM、运维脚本常硬编码列顺序,漏掉它导致误判。真正可靠的方式只有:
SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1' AND INDEX_NAME = 'idx_name'; —— 返回 IS_VISIBLE = 'NO' 才算成功。刚执行完 ALTER 可能有毫秒级延迟,更稳的验证是跑一次 EXPLAIN SELECT * FROM t1 WHERE col = 1:如果之前走该索引,现在变成 type: ALL 或换用别的索引,基本可确认生效。
FORCE INDEX 会绕过不可见性,必须提前全局清理
这是上线前最常踩的坑:FORCE INDEX (idx_name) 不管索引是否 INVISIBLE,都会尝试强制使用,结果报错 ERROR 1176 (42000): Key 'idx_name' doesn't exist in table 't1'。这不是语法错误,是优化器在可见性过滤阶段已注销该索引,FORCE 指令找不到目标。
必须在设为不可见前做三件事:
- 全局扫描所有 SQL 文件、配置文件、ORM 映射层、存储过程,移除所有
FORCE INDEX和USE INDEX提示 - 在测试环境开启
general_log或用pt-query-digest抓取真实执行语句,确认无残留 - 注意备份恢复后状态可能意外失效:mysqldump 默认保留 INVISIBLE 属性,但 xtrabackup 等物理备份工具不保存该元数据,恢复后索引可能自动变回 VISIBLE,需立即核对
INFORMATION_SCHEMA.STATISTICS.IS_VISIBLE
INVISIBLE 索引不是“省资源”,而是“控风险”——它不减少写入开销、不释放磁盘空间、不跳过唯一性检查,只解决“删之前不敢试”这个卡点。真正容易被忽略的是触发器内嵌的 SELECT,它不受会话级 optimizer_switch 控制,必须单独捕获其执行计划。











