MySQL 索引优化实战——让查询速度提升100倍

📅 2026/6/18 20:35:54
MySQL 索引优化实战——让查询速度提升100倍
后端开发中SQL 慢查询是性能问题的头号杀手。90% 的性能问题都能通过合理的索引解决。这篇文章从实战出发帮你搞懂 MySQL 索引的原理和优化方法。一、为什么索引能提速没有索引时MySQL 要逐行扫描整张表才能找到数据全表扫描。有索引后通过 BTree 结构直接定位到目标数据。数据量无索引全表扫描有索引BTree1万行~10ms1ms100万行~500ms~1ms1000万行~5秒~2ms二、索引类型-- 1. 主键索引自动创建CREATETABLEuser(idBIGINTPRIMARYKEYAUTO_INCREMENT);-- 2. 普通索引CREATEINDEXidx_nameONuser(name);-- 3. 唯一索引CREATEUNIQUEINDEXidx_emailONuser(email);-- 4. 联合索引CREATEINDEXidx_name_ageONuser(name,age);-- 5. 全文索引用于大文本搜索CREATEFULLTEXTINDEXidx_contentONarticle(content);三、最左前缀原则联合索引(name, age, city)相当于创建了三个索引-- ✅ 用到索引WHEREname张三WHEREname张三ANDage25WHEREname张三ANDage25ANDcity郑州-- ❌ 用不到索引WHEREage25WHEREcity郑州WHEREage25ANDcity郑州核心联合索引从最左列开始匹配跳过任何一列后面的列就失效了。四、常见索引失效场景1. 对索引列做了运算-- ❌ 失效SELECT*FROMuserWHEREage120;-- ✅ 有效SELECT*FROMuserWHEREage19;2. 使用了 LIKE 前置模糊匹配-- ✅ 有效SELECT*FROMuserWHEREnameLIKE张%;-- ❌ 失效SELECT*FROMuserWHEREnameLIKE%三;SELECT*FROMuserWHEREnameLIKE%三%;3. 使用了函数-- ❌ 失效SELECT*FROMuserWHERESUBSTR(name,1,1)张;-- ✅ 有效SELECT*FROMuserWHEREnameLIKE张%;4. 类型不一致-- 假设 phone 是 VARCHAR 类型-- ❌ 失效隐式类型转换SELECT*FROMuserWHEREphone13800008888;-- ✅ 有效SELECT*FROMuserWHEREphone13800008888;五、通过 EXPLAIN 分析慢查询EXPLAINSELECT*FROMuserWHEREname张三\G重点看这几列列好坏typeconst, ref, rangeALL全表扫描rows越小越好几十万就要注意ExtraUsing indexUsing filesort需要优化possible_keys有值null没用到索引实战分析-- 先创建一个模拟表CREATETABLEorders(idBIGINTPRIMARYKEYAUTO_INCREMENT,order_noVARCHAR(64),user_idBIGINT,statusTINYINT,amountDECIMAL(10,2),created_atDATETIME);-- 插入10万条测试数据-- 省略插入语句实际可以用存储过程-- 检查慢查询EXPLAINSELECT*FROMordersWHEREstatus1;-- type: ALL, rows: 100000 → 全表扫描需要优化-- 加索引后CREATEINDEXidx_statusONorders(status);EXPLAINSELECT*FROMordersWHEREstatus1;-- type: ref, rows: 50000 → 用了索引扫描行数减半六、优化实战场景场景1分页查询太慢-- 慢OFFSET 越大越慢SELECT*FROMordersORDERBYidLIMIT10000,20;-- 快用上一页的最大 ID 做条件SELECT*FROMordersWHEREid10000ORDERBYidLIMIT20;方式100页1000页10000页OFFSET~30ms~200ms~2sID条件~5ms~5ms~5ms场景2排序导致文件排序-- 需要建立 (status, created_at) 联合索引-- 避免 Using filesortSELECT*FROMordersWHEREstatus1ORDERBYcreated_atDESCLIMIT10;CREATEINDEXidx_status_createONorders(status,created_at);场景3分组统计优化-- 给 group by 的列加索引SELECTuser_id,COUNT(*)FROMordersGROUPBYuser_id;CREATEINDEXidx_user_idONorders(user_id);七、索引设计原则1. 不是越多越好一张表的索引数量建议控制在5个以内。索引太多会导致插入/更新变慢每次都要维护索引占用磁盘空间查询优化器可能选错索引2. 高区分度的列才适合建索引-- 区分度差只有0和1两个值索引意义不大-- 索引扫描也要扫一半数据不如全表扫描CREATEINDEXidx_statusONorders(status);-- 区分度高每个值都不同索引效果最好CREATEINDEXidx_order_noONorders(order_no);3. 优先给 Where 和 Join 的列建索引-- WHERE 条件列SELECT*FROMuserWHEREname张三;-- JOIN 关联列SELECT*FROMorderoLEFTJOINuseruONo.user_idu.id;-- user.id 要有索引总结索引优化是后端开发的核心技能记住三条联合索引遵循最左前缀原则—— 把最常查询的列放最左边用 EXPLAIN 分析慢查询—— 重点关注 type 和 rows不是越多越好—— 一张表 5 个以内索引足矣建议在日常开发中每个 SQL 都养成用 EXPLAIN 看一眼的习惯。如果对你有帮助欢迎点赞、评论、关注【张老师技术栈】持续分享 Java/Python/爬虫 实战干货。