news 2026/8/7 3:34:02

MySQL 索引真不是越多越好!聊聊索引的 “隐形代价”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 索引真不是越多越好!聊聊索引的 “隐形代价”
    • 先搞懂:索引为啥不能多?
      • 1. 写入/更新/删除速度直接变慢
      • 2. 占用大量磁盘空间
      • 3. 让MySQL优化器“选花眼”
      • 4. 浪费内存缓存
    • 哪些是“无用索引”?常见的冗余情况
      • 1. 重复索引
      • 2. 功能重复的索引
    • 怎么判断索引是否过多?实用检查方法
      • 1. 看索引数量和数据量的匹配
      • 2. 查索引使用率
      • 3. 找未使用的索引
    • 建索引的“黄金法则”:少而精才是王道
      • 1. 按业务类型决定索引数量
      • 2. 索引优先级排序(按重要性来)
      • 3. 精简索引的实战例子
      • 4. 索引管理的小技巧
    • 最后总结下

刚接触MySQL的时候,我总觉得索引建得越多越好,不管什么查询,先给字段加个索引再说。后来做项目时发现,表的写入速度越来越慢,甚至有些查询明明加了索引,却比没加还慢——这才明白,索引不是“万能钥匙”,多了反而会帮倒忙。今天就聊聊索引过多的问题,再分享些实用的建索引技巧。

先搞懂:索引为啥不能多?

索引本质是帮查询“快速定位”的数据结构,但它不是白给的,每加一个索引,都要付出对应的代价。

1. 写入/更新/删除速度直接变慢

我认为这是索引过多最明显的影响。数据写入的时候,不光要写主数据,还要给每个索引都更新一遍——就像给文件建了多个目录,新增文件时每个目录都得改,肯定费时间。

看个直观的例子,假设用户表有5个索引,执行一条插入语句:

INSERTINTOusers(name,age,city,phone,email)VALUES('张三',25,'北京','13800138000','zhangsan@example.com');

实际执行时,数据库要做6次写入操作:1次写主数据,5次更新对应的索引。

更新和删除也是同理,如果更新的字段有索引,得先删除旧的索引记录,再插入新的。我们的经验是,索引数量和写入耗时基本成正比,比如:

  • 无索引:插入1000条0.1秒
  • 1个索引:插入1000条0.2秒
  • 5个索引:插入1000条0.6秒
    多一个索引,写入压力就大一分。

2. 占用大量磁盘空间

索引本身也要存数据,而且数量多了之后,占用的空间可能比数据本身还大。我之前查过公司一个表,数据才100MB,5个索引加起来居然占了100多MB,相当于数据体积翻倍。

教大家一个查询索引占用空间的SQL,自己就能排查:

SELECTtable_name,index_name,ROUND(index_length/1024/1024,2)AS'索引大小(MB)',ROUND(data_length/1024/1024,2)AS'数据大小(MB)',ROUND(index_length/data_length,2)AS'索引/数据比'FROMinformation_schema.TABLESWHEREtable_schema='你的数据库名';

健康的情况应该是索引大小小于数据大小,如果索引比数据还大,就得考虑精简了。

3. 让MySQL优化器“选花眼”

MySQL执行查询前,优化器会先选一个最优索引。如果表有10个甚至更多索引,优化器得一个个评估哪个最合适,这个过程本身就耗CPU。

更麻烦的是,优化器可能选错索引。比如表有idx_age(age)、idx_city(city)、idx_age_city(age,city)三个索引,执行SELECT * FROM users WHERE age > 20 AND city = '北京'时,优化器可能误选idx_ageidx_city,而不是更合适的联合索引idx_age_city,导致查询变慢。

4. 浪费内存缓存

InnoDB的缓冲池是有限的,会缓存常用的数据和索引。如果索引太多,大量不常用的索引会占满缓冲池,导致真正需要的热数据没法被缓存,查询时得从磁盘读,速度自然慢。

比如缓冲池总共4GB,本来3GB缓存数据、1GB缓存常用索引,查询很快;但索引多了之后,2GB都被不常用的索引占了,数据缓存只剩2GB,频繁出现磁盘IO,性能直接下滑。

哪些是“无用索引”?常见的冗余情况

很多时候索引过多,是因为建了重复或冗余的索引,自己却没发现。

1. 重复索引

比如先建了单列索引idx_a (a),又建了联合索引idx_a_b (a,b)——idx_a就是重复索引,因为联合索引idx_a_b的前缀列是a,完全能覆盖idx_a的查询场景,留着idx_a只会增加维护成本。

2. 功能重复的索引

比如idx_a_b (a,b)idx_b_a (b,a),如果查询条件主要是a开头或b开头,可能都需要,但如果很少用b开头的查询,idx_b_a就是多余的。

还有比如idx_name (name)idx_name_part (name(20)),都是给name字段建索引,留一个合适的就行,没必要建两个。

怎么判断索引是否过多?实用检查方法

光说不行,得有具体的判断标准,分享几个我平时常用的方法。

1. 看索引数量和数据量的匹配

不同规模的表,适合的索引数量不一样:

  • 小型表(<10万行):3-5个索引足够,数据少全表扫描都快,多建索引没必要
  • 中型表(10万-1000万行):5-8个索引,平衡读写性能
  • 大型表(>1000万行):8-12个索引,再多就会严重影响写入

2. 查索引使用率

用这条SQL能看到每个索引的使用情况, rows_read很低说明索引很少被用:

SELECTobject_schemaAS数据库名,object_nameAS表名,index_nameAS索引名,rows_readAS读取次数,rows_insertedAS插入次数,rows_updatedAS更新次数,rows_deletedAS删除次数FROMperformance_schema.table_io_waits_summary_by_index_usageWHEREindex_nameISNOTNULLORDERBYrows_readDESC;

如果某个索引的rows_read是0,或者远小于rows_updated,基本就是无用索引。

3. 找未使用的索引

MySQL 8.0+有个很方便的视图,能直接查出未使用的索引:

SELECT*FROMsys.schema_unused_indexes;

低于8.0版本的话,用这条SQL手动排查:

SELECTs.index_name,s.table_name,s.rows_selected,CASEWHENs.rows_selected<1000THEN'考虑删除'WHENs.rows_selected<10000THEN'观察'ELSE'保留'ENDAS建议FROM(SELECTOBJECT_NAMEAStable_name,INDEX_NAMEASindex_name,COUNT_READASrows_selected,COUNT_UPDATEASrows_updatedFROMperformance_schema.table_io_waits_summary_by_index_usageWHEREOBJECT_SCHEMA=DATABASE())sWHERErows_selected=0OR(rows_selected<100ANDrows_updated>1000);

建索引的“黄金法则”:少而精才是王道

我认为建索引的核心是“按需创建”,不是越多越好,而是要精准。分享几个实用的策略:

1. 按业务类型决定索引数量

  • OLTP系统(比如电商交易、用户中心,频繁写入):索引要少而精,3-8个足够,只给高频查询建索引
  • OLAP系统(比如报表分析、数据仓库,频繁读取):索引可以稍多,8-15个,覆盖多种查询模式,但也要控制维护成本

2. 索引优先级排序(按重要性来)

  1. 主键索引(必须有,默认会建)
  2. 唯一约束索引(比如用户手机号、身份证号,保证数据唯一)
  3. 高频查询的WHERE条件列(比如订单表的user_id、商品表的category_id)
  4. 高频查询的JOIN条件列(比如关联查询时的外键)
  5. 高频的ORDER BY/GROUP BY列(避免排序耗时)
  6. 覆盖索引(查询的列都在索引里,不用回表,比如SELECT age,city FROM users WHERE age>25,建idx_age_city就能覆盖)
  7. 低选择性列(比如性别,选值只有男/女,建索引效果差,通常不需要)

3. 精简索引的实战例子

之前见过一个电商商品表,居然建了15个索引,包括各种单列索引和重复的联合索引,导致写入很慢。优化后精简到6个核心索引,效果立竿见影:

优化前(反面例子):

CREATETABLEproducts(idINTPRIMARYKEY,nameVARCHAR(200),category_idINT,priceDECIMAL(10,2),stockINT,statusTINYINT,created_timeDATETIME,-- 冗余索引太多INDEXidx_name(name),INDEXidx_category(category_id),INDEXidx_price(price),INDEXidx_cat_price(category_id,price),INDEXidx_cat_status(category_id,status),INDEXidx_name_part(name(20))-- 和idx_name重复);

优化后(正面例子):

CREATETABLEproducts(idINTPRIMARYKEY,nameVARCHAR(200),category_idINT,priceDECIMAL(10,2),stockINT,statusTINYINT,created_timeDATETIME,-- 6个核心索引,覆盖所有高频查询INDEXidx_cat_price_status(category_id,price,status),-- 分类+价格+状态查询INDEXidx_status_created(status,created_time),-- 状态+创建时间查询INDEXidx_name(name(50)),-- 商品名称搜索INDEXidx_stock_status(stock,status),-- 库存预警查询INDEXidx_created(created_time),-- 时间范围查询INDEXidx_cat_created(category_id,created_time)-- 分类+时间查询);

优化后写入速度提升40%,内存占用减少60%,查询性能却没下降。

4. 索引管理的小技巧

  • 定期审计:每月查一次未使用的索引,及时清理
  • 用不可见索引测试:删除索引前,先设为不可见(ALTER TABLE users ALTER INDEX idx_test INVISIBLE),观察一段时间业务无影响再删除
  • 建索引前问自己几个问题:这个查询每天执行超过100次吗?有类似索引能复用吗?表写入频繁吗?索引列选择性高吗(比如性别选择性低,不适合建)?

最后总结下

索引就像调料,放对了能提味,放多了反而毁菜。它不是越多越好,而是要在查询性能和写入成本之间找平衡。

我认为核心要点就几个:

  1. 每个索引都是“负债”,要承担维护成本,不是必需的就别建
  2. 优先建联合索引,一个设计好的联合索引能顶好几个单列索引
  3. 定期清理无用、冗余的索引,就像定期大扫除
  4. 写入频繁的表,索引一定要精简;查询频繁的表,可适当多建,但也要控制数量

其实建索引没有绝对的标准,关键是结合自己的业务场景,按需创建、定期优化。如果盲目建一堆索引,最后只会导致整个数据库性能下滑,得不偿失。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/2 8:55:47

监控舆情动态:及时回应社区关于模型能力的质疑声音

监控舆情动态&#xff1a;及时回应社区关于模型能力的质疑声音 在开源大模型社区&#xff0c;一个1.5B参数的轻量级模型突然在多个高难度数学与编程基准上超越部分百亿甚至千亿参数模型——这听起来像天方夜谭。然而&#xff0c;VibeThinker-1.5B-APP 的出现&#xff0c;正是这…

作者头像 李华
网站建设 2026/7/26 5:59:35

【Docker Rollout部署实战指南】:掌握高效应用发布的5大核心命令

第一章&#xff1a;Docker Rollout部署的核心概念与价值Docker Rollout部署是一种基于容器化技术的渐进式发布策略&#xff0c;旨在实现应用更新过程中的高可用性与低风险交付。通过将新版本服务以容器形式逐步推送到生产环境&#xff0c;团队能够在监控关键指标的同时控制流量…

作者头像 李华
网站建设 2026/7/31 14:29:41

BBDown终极指南:快速掌握B站视频下载技巧

BBDown终极指南&#xff1a;快速掌握B站视频下载技巧 【免费下载链接】BBDown Bilibili Downloader. 一款命令行式哔哩哔哩下载器. 项目地址: https://gitcode.com/gh_mirrors/bb/BBDown 还在为无法离线观看B站精彩内容而烦恼吗&#xff1f;想要轻松保存喜爱的视频用于学…

作者头像 李华
网站建设 2026/8/2 2:46:47

从崩溃到稳定:Dify+Next.js错误边界与日志追踪完整实施方案

第一章&#xff1a;Dify与Next.js错误处理的现状与挑战在现代全栈应用开发中&#xff0c;Dify 作为 AI 应用开发平台&#xff0c;与 Next.js 这类支持 SSR 和 API 路由的框架深度集成&#xff0c;带来了灵活的开发体验&#xff0c;同时也对错误处理机制提出了更高要求。由于 Di…

作者头像 李华
网站建设 2026/7/29 21:23:02

为什么你的Excel在Dify中无法加载?,这7个常见问题必须避开

第一章&#xff1a;为什么你的Excel在Dify中无法加载&#xff1f;在将Excel文件集成到Dify平台时&#xff0c;许多用户遇到文件无法加载的问题。这通常并非由单一原因导致&#xff0c;而是涉及文件格式、编码方式、网络配置及平台限制等多方面因素。文件格式与扩展名不匹配 Dif…

作者头像 李华
网站建设 2026/8/5 19:28:21

3步极速配置:轻松搭建Firefox自动化测试环境

3步极速配置&#xff1a;轻松搭建Firefox自动化测试环境 【免费下载链接】geckodriver WebDriver for Firefox 项目地址: https://gitcode.com/gh_mirrors/ge/geckodriver 还在为Firefox自动化测试环境配置而烦恼吗&#xff1f;作为WebDriver for Firefox的核心组件&…

作者头像 李华