游乐游手机版
首页/数据库/文章详情

MySQL索引使用规则与最佳实践详解

时间:2026-06-10 07:02
MySQL索引需遵循最左前缀法则,联合索引从最左列连续匹配;范围查询可能影响右侧列索引使用;避免在索引列使用函数、隐式类型转换、头部模糊查询;覆盖索引可减少回表;前缀索引需权衡长度与区分度;设计索引应围绕高频查询场景,控制数量并关注执行计划。

在数据库调优这条路上,索引绝对是绕不开的核心话题。搭建索引的门槛不高,但真正用好它,需要对MySQL的索引机制和优化器行为有比较清晰的认识。这篇文章主要想系统整理一下:联合索引的匹配规则、哪些SQL写法会导致索引失效、覆盖索引为什么能减少回表,以及实际建索引时应遵循哪些原则。

MySQL索引使用规则和最佳实践

前言

索引能提升查询效率,这不假,但建了索引不等于它一定会被用上。MySQL最终能不能走索引,还要看查询条件怎么写的、返回哪些字段、排序分组方式,以及优化器对成本的综合判断。换句话说,索引是工具,能不能用好,取决于写SQL的人。

一、联合索引的核心:最左前缀法则

联合索引也叫复合索引,一个索引里包含多个列。比如:

create index idx_user_pro_age on tb_user(profession, age);

这个索引的列顺序是profession、age。使用时要遵守最左前缀法则,简单来说,查询条件必须从索引最左边的列开始,并且不能跳过中间列。

explain select * from tb_user where profession = '软件工程';
explain select * from tb_user where profession = '软件工程' and age = 25;
explain select * from tb_user where age = 25;

前两个查询都能从profession开始匹配联合索引。第三个查询只用了age,跳过了最左边的profession,通常无法按这个联合索引进行高效查找。MySQL官方文档也有类似的说明:如果有一个三列索引col1、col2、col3,那么它可以用于col1、col1+col2、col1+col2+col3这种左侧连续组合;只查col2或col3,就不符合左侧前缀。

最左前缀不是”用了联合索引“这么简单,而是”从联合索引最左列开始连续匹配“。

二、范围查询会影响右侧列继续匹配

联合索引中,如果某一列使用了范围查询,右侧列可能无法继续作为高效定位条件。

例如有以下联合索引:

create index idx_user_pro_age_status on tb_user(profession, age, status);

查询语句如下:

explain select * from tb_user where profession = '软件工程' and age > 25 and status = '启用';

profession是等值查询,可以先使用。age是范围查询,MySQL会在age范围内查找数据。status虽然也写在查询条件中,但它在范围条件右侧,通常不能继续参与完整的索引定位。有些资料提到把大于、小于改成大于等于、小于等于,可能在部分场景中让执行计划表现不同,但不能当成固定优化公式。大于等于、小于等于本质上也属于范围条件,最终还是要看EXPLAIN的结果。

范围条件后面的列,不一定还能继续作为高效定位条件。

三、常见索引失效情况

索引失效并不是索引被删除了,而是这条SQL无法按预期利用索引。常见情况有以下几类。

1. 在索引列上进行运算或函数处理

explain select * from tb_user where substring(phone, 10, 2) = '15';

phone字段如果有索引,这里很难直接利用。因为MySQL不是拿原始phone值去匹配,而是先对phone做substring处理,再比较结果。写SQL时要尽量让索引列保持原样,不要在索引列外面套函数、运算表达式。

2. 字符串类型字段不加引号

explain select * from tb_user where phone = 17799990015;

如果phone是字符串类型,这里没有加引号,可能触发隐式类型转换,一旦发生,就可能让索引无法按原本的字符串规则使用。正确写法应该是:

explain select * from tb_user where phone = '17799990015';

3. 头部模糊查询

explain select * from tb_user where profession like '%工程';
explain select * from tb_user where profession like '%工程%';

这两种写法都在前面加了百分号,MySQL无法从索引的起点开始匹配,索引效果明显变差。如果只是尾部模糊匹配,通常更容易利用索引:

explain select * from tb_user where profession like '软件%';

4. or条件中有一侧没有索引

explain select * from tb_user where phone = '17799990015' or address = '北京';

如果phone有索引,但address没有索引,优化器可能认为走索引意义不大,最后选全表扫描。or查询不是一定不能用索引,关键要看or两侧字段是否都有合适索引,以及优化器评估后的成本。

5. 优化器认为全表扫描更快

有时候SQL写法没有明显问题,但MySQL仍然不走索引。原因可能是数据量太小、条件区分度太低,或者优化器认为全表扫描比走索引再回表更快。判断索引是否生效,不能只看“建没建索引”,还要看执行计划里的key、rows、Extra等信息。

四、SQL提示:use、ignore、force index

当一个字段既有单列索引,又在联合索引里出现时,MySQL会根据优化器成本选择索引。如果确实想影响优化器的选择,可以使用索引提示。

explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';
explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工程';
explain select * from tb_user force index(idx_user_pro) where profession = '软件工程';

use index是建议使用某个索引,但优化器仍然可能选择别的方案。ignore index是告诉优化器不要考虑某个索引。force index是更强的提示,表示强制优先使用指定索引。不过,强制使用索引不等于一定更快,如果数据区分度很低,或者回表成本很高,强行走索引反而可能变慢。实际使用时要结合执行计划和查询耗时一起判断。

五、覆盖索引:为什么尽量少写select *

覆盖索引,指的是查询使用了索引,并且需要返回的字段都能从索引中拿到,不需要再回到表里查询完整行数据。

例如表中有id、username、password、status四个字段,现在要优化这条SQL:

select id, username, password from tb_user where username = 'itcast';

可以考虑建立联合索引:

create index idx_user_name_pwd on tb_user(username, password);

如果id是主键,在InnoDB的二级索引中会保存主键值。这样查询username、password、id时,就可能直接从索引中拿到结果,不需要回表查询status等其他字段。这也是为什么不建议随手写select *。返回字段越多,越容易超出索引本身能提供的范围,最后就需要回表。

执行计划Extra字段里常见两个信息:Using index condition表示使用了索引条件下推,但仍可能需要读取完整行;Using where; Using index通常表示查询需要的字段可以从索引中拿到,不需要回表。

覆盖索引的价值,就是少一次回表。

六、前缀索引:长字符串字段怎么建索引

如果字段是varchar、text这类字符串类型,而且内容比较长,直接给整列建索引会占用更多空间,也会增加磁盘IO。这时可以使用前缀索引,只取字段前N个字符建立索引。

create index idx_email on tb_user(email(5));

前缀长度不是随便写的,要看区分度。可以先计算完整字段的选择性:

select count(distinct email) / count(*) from tb_user;

再计算不同前缀长度的选择性:

select count(distinct substring(email, 1, 5)) / count(*) from tb_user;
select count(distinct substring(email, 1, 8)) / count(*) from tb_user;
select count(distinct substring(email, 1, 10)) / count(*) from tb_user;

选择性越接近完整字段,说明这个前缀长度越能区分数据。前缀太短,重复值多,过滤效果差;前缀太长,索引空间节省不明显。创建后可以通过show index查看sub_part,确认前缀索引截取的长度。

七、不完全满足最左前缀时,为什么有时看起来还走了索引

最左前缀法则是联合索引用于高效查找的基本规则,但实际执行计划里,有时即使SQL没有完全满足最左前缀,也可能看到MySQL使用了某个索引。这不代表最左前缀法则失效了,而是优化器可能在其他角度利用索引。

第一种情况是覆盖索引。如果查询字段都在联合索引中,即使where条件没有从最左列开始,MySQL也可能扫描整个索引来返回数据。因为扫描索引比扫描整张表更轻。

create index idx_abc on tb_demo(a, b, c);
select b, c from tb_demo where b = 10;

这里where条件没有使用a,不满足最左前缀。但如果只返回b、c,优化器可能选择扫描idx_abc,因为索引本身已经包含需要的字段。

第二种情况是索引下推(ICP)。MySQL 5.6之后支持ICP,它可以把一部分索引列上的过滤条件下推到存储引擎层,先在索引层过滤一批数据,减少回表次数。

select * from tb_demo where a = 1 and c = 3;

如果索引是a、b、c,a可以按最左前缀使用,c虽然跳过了b,但仍可能通过索引下推参与过滤。开启状态可以关注optimizer_switch=index_condition_pushdown=on

第三种情况是排序或分组。如果order by、group by的字段顺序和联合索引顺序匹配,优化器可能利用索引顺序减少额外排序。不过这类优化对字段顺序、排序方向、where条件都有要求,不能只看“字段在索引里”就认为一定能避免排序。

所以,看到执行计划里使用了索引时,还要继续看type、key_len、rows和Extra。它可能是高效定位,也可能只是全索引扫描。

八、单列索引和联合索引怎么选

单列索引是一个索引只包含一个字段,联合索引是一个索引包含多个字段。如果业务里经常按多个条件组合查询,通常优先考虑联合索引,而不是给每个字段都单独建一个索引。

例如经常按职业、年龄、状态查询:

select * from tb_user where profession = '软件工程' and age = 25 and status = '启用';

比起分别给profession、age、status建三个单列索引,更常见的做法是根据查询频率和区分度建立一个联合索引:

create index idx_user_pro_age_status on tb_user(profession, age, status);

联合索引的好处是可以同时服务多条件过滤,并且在返回字段合适时形成覆盖索引,减少回表。但联合索引也不是越长越好,索引列越多,维护成本越高,插入、更新、删除数据时都要维护对应索引结构。

九、索引设计原则

索引设计可以按下面几条来判断:

  1. 数据量较大,并且查询比较频繁的表,才更有必要建立索引。
  2. 经常出现在where、order by、group by后面的字段,优先考虑索引。
  3. 尽量选择区分度高的列,例如手机号、用户名这类重复率低的字段。
  4. 字符串字段较长时,可以考虑前缀索引。
  5. 多条件查询优先考虑联合索引,减少多个单列索引堆叠。
  6. 控制索引数量,索引会提升查询,但也会降低增删改效率。
  7. 如果索引列业务上不允许为空,建表时可以声明NOT NULL。

索引设计不是“给字段都建上”,而是围绕查询场景选择最少、最有效的索引。

十、实际排查时怎么判断索引用得好不好

平时排查SQL时,可以先看四个点。

第一,看possible_keys和key。possible_keys表示可能用到的索引,key表示最终实际选择的索引。

第二,看key_len。它可以帮助判断联合索引大概使用到了哪些列。尤其是联合索引中间出现范围查询、跳过字段时,key_len很有参考价值。

第三,看rows。rows越大,说明MySQL预计要扫描的数据越多。即使走了索引,如果rows很大,查询也不一定快。

第四,看Extra。Extra里如果出现覆盖索引、索引条件下推、临时表、文件排序等信息,都能帮助判断这条SQL还有没有优化空间。

总结

MySQL索引使用规则可以压缩成一句话:先看查询条件是否满足最左前缀,再看有没有函数、隐式转换、头部模糊、or条件这些失效写法,最后结合返回字段判断能不能形成覆盖索引。

建索引时不要只盯着某一个字段,而要把where条件、返回字段、排序分组和字段区分度放在一起看。真正好用的索引,通常不是数量最多的索引,而是刚好匹配高频查询场景、维护成本又可控的索引。

参考资料:

  • MySQL 8.0 Reference Manual: Multiple-Column Indexes
  • MySQL 8.0 Reference Manual: Index Hints
  • MySQL 8.0 Reference Manual: Index Condition Pushdown Optimization
来源:https://www.jb51.net/database/365438lqf.htm
上一篇从MySQL迁移到PostgreSQL的全面原因详解 下一篇MySQL MVCC多版本并发控制实现机制详解
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
MyBatis Hive多表关联实现方法
数据库 · 2026-07-01

MyBatis Hive多表关联实现方法

MyBatis处理Hive多表关联查询与普通数据库类似。需准备映射文件,使用association和collection标签定义关联;创建Java实体类包含集合成员变量承接一对多关系;编写Mapper接口声明查询方法;配置MyBatis环境注册映射;最后通过SqlSession调用即可获取关联数据。

提升Hive Metastore查询速度的有效方法
数据库 · 2026-07-01

提升Hive Metastore查询速度的有效方法

HiveMetastore查询优化需从存储优化、缓存机制、查询策略、索引构建、并行能力、配置调优、硬件升级、数据分区及定期维护等多方面协同入手,综合提升系统吞吐量与响应速度,有效降低查询延迟。

Hive Metastore处理大数据的核心机制
数据库 · 2026-07-01

Hive Metastore处理大数据的核心机制

HiveMetastore管理元数据,通过分库分表、读写分离应对海量元数据,调整JVM堆内存并采用G1GC提升稳定性,利用HDFS或云存储及CBO优化器加速查询,在大数据场景下提供高效元数据服务。

Kafka Coordinator 如何监控集群的完整方法与最佳实践指南
数据库 · 2026-07-01

Kafka Coordinator 如何监控集群的完整方法与最佳实践指南

Kafka协调器监控可通过命令行工具、KafkaManager及JMX实时查看消费者滞后、分区状态等性能指标,并利用Prometheus+Grafana实现长期可视化监控与告警,从而确保集群稳定运行。

Hive中row_number()函数性能的实用高效监控方法与优化技巧
数据库 · 2026-07-01

Hive中row_number()函数性能的实用高效监控方法与优化技巧

Hive中row_number()性能受数据量、索引、查询复杂度及数据倾斜影响。优化需通过分区、建索引、查询优化、使用ORC Parquet格式及调整CBO和并行度实现。监控可借助HiveWebUI、YARN界面、日志或第三方工具定位瓶颈,持续迭代改进。