pt-duplicate-key-checker 不能直接删索引,仅检测并输出 DROP INDEX 建议语句,需人工确认执行;默认只报冗余索引,重复索引需加 --verbose 才识别;不支持函数索引和不可见索引,存在误判风险。

pt-duplicate-key-checker 能不能直接删索引
不能。它只负责检测和报告,不执行任何 DDL 操作。pt-duplicate-key-checker 输出的是建议语句(比如 DROP INDEX),但不会自动运行——这点必须手动确认,否则可能误删主键或唯一约束依赖的索引。
常见错误现象:有人把输出结果复制进 MySQL 客户端批量执行,结果删掉了 PRIMARY KEY 对应的隐式索引,导致表无法写入;或者删了外键引用的索引,引发 ERROR 150。
- 使用前务必检查每条建议是否影响主键、唯一约束、外键或查询性能热点
- 建议先用
--dry-run参数看输出,再加--print确认 SQL 内容 - 生产环境操作前,先在从库或测试库跑一遍,观察慢查询日志是否有新增告警
重复索引 vs 冗余索引的区别在哪
重复索引指完全相同的列顺序和类型,比如 INDEX idx_a_b (a,b) 和另一个同名或不同名但定义一模一样的 INDEX idx_x (a,b);冗余索引则是被另一个索引“覆盖”的情况,比如已有 INDEX idx_a_b_c (a,b,c),又建了 INDEX idx_a_b (a,b) —— 后者就是冗余的,因为前缀已包含。
容易踩的坑:工具默认只报冗余,不报重复;而有些 ORM 自动生成索引时会悄悄建出重复项(比如 Laravel 的 foreignId + index() 组合),pt-duplicate-key-checker 需配合 --verbose 才能识别。
-
--verbose会显示索引字段的完整定义(含长度、排序方向),用于比对是否真正重复 - 注意前缀索引:如果一个是
name(10),另一个是name(20),不算冗余,但可能影响查询选择性 - 复合索引中
ASC/DESC不一致(MySQL 8.0+ 支持)会导致不被识别为冗余,需人工核对
为什么删了冗余索引后查询反而变慢
因为优化器选错了执行计划,或者应用里有隐式依赖。比如某个 ORDER BY a,b LIMIT 1 查询原本靠 INDEX idx_a_b (a,b) 走索引排序,删掉后只剩 INDEX idx_a_b_c (a,b,c),MySQL 可能因统计信息未更新或代价估算偏差,改走全表扫描。
性能影响不是线性的:索引减少会降低写入开销,但可能让某些关键查询失去高效路径。尤其要注意覆盖索引场景——删掉一个看似冗余的索引,可能让原本能避免回表的查询被迫回表。
- 删索引前用
EXPLAIN FORMAT=JSON对核心 SQL 做 baseline 记录 - 删完立刻查
information_schema.STATISTICS确认索引已消失,再观察SHOW PROFILES或慢日志变化 - 如果用了 Query Cache(已弃用但老系统还有),删索引会清空相关缓存,短期 QPS 波动属正常
MySQL 8.0+ 的隐藏陷阱:函数索引和不可见索引
pt-duplicate-key-checker 当前(v3.7)不解析函数索引(如 INDEX idx_upper_name ((UPPER(name)))),也不识别 INVISIBLE 索引,会把它当成普通索引参与冗余判断,导致误报或漏报。
更麻烦的是:如果一个可见索引和一个不可见索引结构相同,工具会建议删掉“冗余”的那个,但你删的可能是不可见索引——这本身没风险;可万一你手抖删了可见的,就出问题了。
- 运行前先查
SELECT index_name, is_visible FROM information_schema.statistics WHERE table_schema = 'db' AND table_name = 't' - 函数索引必须手工比对表达式,工具输出里的列名是虚拟的(比如
func001),不能直接信 - 不可见索引虽然不参与优化器选择,但仍是物理结构,占空间、拖写入,该删还得删,只是不能靠工具自动判
最麻烦的其实是联合运维场景:DBA 用工具扫出一堆建议,开发却不知道某条 SQL 正依赖那个“冗余”索引做松散索引扫描,沟通断层比语法错误更难排查。










