在MySQL数据库的长期运行过程中,随着业务需求变更,很多早期创建的索引会逐渐不再被查询使用,这些冗余索引不仅会占用额外的磁盘空间,还会在数据插入、更新、删除时增加索引维护的成本,拖慢整体数据库性能。定期清理无用索引是数据库优化中投入产出比很高的操作,不过需要掌握正确的方法和风险规避策略。

如何快速定位不再使用的索引
要删除无用索引,首先需要准确识别哪些索引已经不再被使用,MySQL提供了多种方式帮助我们完成这个工作。
利用performance_schema统计索引使用情况
MySQL的performance_schema库中有专门的表记录索引的使用情况,我们可以通过查询table_io_waits_summary_by_index_usage表来获取索引的使用数据,没有被使用的索引对应的COUNT_READ、COUNT_WRITE等字段值会长期为0。
以下是查询指定库中未使用索引的示例SQL:
-- 查询test库中未被使用的索引
SELECT
OBJECT_SCHEMA AS 库名,
OBJECT_NAME AS 表名,
INDEX_NAME AS 索引名,
COUNT_READ AS 读取次数,
COUNT_WRITE AS 写入次数
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = 'test'
AND INDEX_NAME IS NOT NULL
AND COUNT_READ = 0
AND COUNT_WRITE = 0
AND INDEX_NAME != 'PRIMARY';
使用pt-index-usage工具分析慢查询日志
Percona Toolkit中的pt-index-usage工具可以分析慢查询日志,统计哪些索引被查询使用过,未被提及的索引大概率是无用索引。使用该工具前需要先开启慢查询日志,执行以下命令开启:
-- 开启慢查询日志 SET GLOBAL slow_query_log = 'ON'; -- 设置慢查询阈值,单位秒 SET GLOBAL long_query_time = 1; -- 设置慢查询日志存储位置 SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';
然后执行工具分析日志:
pt-index-usage /var/lib/mysql/slow.log --host=127.0.0.1 --user=root --password=123456
快速删除无用索引的操作方法
确认索引无用后,可以使用DROP INDEX语句快速删除索引,语法格式如下:
-- 删除普通索引 DROP INDEX 索引名 ON 表名; -- 删除主键索引(需要特殊处理) ALTER TABLE 表名 DROP PRIMARY KEY;
如果需要批量删除多个无用索引,可以拼接删除语句,以下是生成批量删除语句的示例:
-- 生成删除test库无用索引的批量语句
SELECT
CONCAT('DROP INDEX ', INDEX_NAME, ' ON ', TABLE_NAME, ';') AS 删除语句
FROM (
SELECT
OBJECT_NAME AS TABLE_NAME,
INDEX_NAME AS INDEX_NAME
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = 'test'
AND INDEX_NAME IS NOT NULL
AND COUNT_READ = 0
AND COUNT_WRITE = 0
AND INDEX_NAME != 'PRIMARY'
) AS tmp;
索引删除的风险及规避方案
删除索引看似操作简单,但如果处理不当会引发严重的业务问题,以下是常见的风险和对应的规避方法。
风险1:误删仍被使用的索引,导致查询性能骤降
如果索引实际还在被部分低频查询使用,误删后这些查询会转为全表扫描,查询耗时可能从毫秒级上升到秒级甚至分钟级,严重影响业务响应速度。
规避方案:删除前先确认索引的冷门程度,对于使用频率极低的索引,可以先将其重命名标记为待删除状态,观察1-2周业务无异常后再执行删除。重命名索引的示例:
-- 重命名索引,标记为待删除 ALTER TABLE test.user_table RENAME INDEX idx_old TO idx_old_to_drop;
风险2:删除索引时锁表,影响线上业务写入
在MySQL 5.7及之前的版本中,删除索引的操作会对表加元数据锁,如果表数据量很大,删除索引的过程可能持续较长时间,期间会阻塞表的写入操作,导致业务报错。
规避方案:尽量在业务低峰期执行删除操作,对于大表,可以使用Online DDL特性,MySQL 5.6及以上版本默认支持Online DDL删除索引,不会长时间阻塞写入。如果使用的是低版本MySQL,可以考虑使用pt-online-schema-change工具执行无锁删除。
风险3:删除主键索引导致数据完整性问题
主键索引是InnoDB表聚簇索引的基础,如果误删主键索引,且没有及时创建新的主键,会导致表没有聚簇索引,查询性能大幅下降,甚至可能出现数据重复的问题。
规避方案:删除主键前必须先确认表有其他唯一非空索引可以替代主键,或者提前创建新的主键索引,再删除旧的主键。删除主键的示例:
-- 先添加新主键 ALTER TABLE test.user_table ADD PRIMARY KEY (user_id); -- 再删除旧主键(如果旧主键不是user_id) ALTER TABLE test.user_table DROP PRIMARY KEY;
风险4:删除索引后未验证,后续问题排查困难
删除索引后如果没有做验证,后续出现查询性能问题时,很难第一时间联想到是索引删除导致的问题,增加排查成本。
规避方案:删除索引前后分别记录核心查询的耗时,删除后对比验证性能无异常,同时记录删除的索引名称、所属表、删除时间、操作人,方便后续追溯。
索引清理的最佳实践
为了保证索引清理的安全和高效,建议遵循以下实践流程:
- 每月定期统计索引使用情况,筛选出连续30天无使用的索引作为待清理候选
- 对待清理索引进行业务侧确认,排除低频但必要的业务查询使用的索引
- 先在测试环境模拟删除操作,验证无问题后再到生产环境执行
- 生产环境删除操作选择凌晨等业务低峰期执行,提前做好数据库备份
- 删除后持续观察1周业务查询性能和错误日志,确认无异常后完成清理
通过以上方法,我们可以在规避风险的前提下,快速完成MySQL无用索引的清理,提升数据库的整体运行效率。