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

 更新时间:2026年06月09日 10:04:48   作者:程序猿乐锅  
这段文章详细介绍了MySQL索引使用规则,包括联合索引的最左前缀法则、范围查询的影响、常见索引失效情况、SQL提示的使用、覆盖索引的价值以及索引设计原则等,感兴趣的朋友一起看看吧

在MySQL中,索引是用来提高数据库查询效率的重要工具。了解和掌握索引的使用规则对于优化数据库性能至关重要。下面是一些关于MySQL索引使用的基本规则和最佳实践:

前言:

索引能提升查询效率,但不是建了索引就一定会用上。MySQL 最终是否走索引,还要看查询条件、返回字段、排序分组方式,以及优化器对成本的判断。

这篇主要整理 MySQL 索引使用规则:联合索引怎么匹配、哪些写法会导致索引失效、覆盖索引为什么能减少回表,以及实际建索引时应该遵守哪些原则。

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

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

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,因为索引本身已经包含需要的字段。

第二种情况是索引下推。

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 索引使用规则和最佳实践的文章就介绍到这了,更多相关mysql索引使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mysql 自定义随机字符串的实现方法

    Mysql 自定义随机字符串的实现方法

    前段时间接了一个项目,需要用到随机字符串,但是mysql的库函数没有直接提供,需要我们自己实现此功能,下面小编给大家介绍下Mysql 自定义随机字符串的实现方法,需要的朋友参考下吧
    2016-08-08
  • MySQL逻辑备份into outfile

    MySQL逻辑备份into outfile

    这篇文章主要介绍了MySQL 备份之 into outfile,文章围绕主题展开详细内容介绍,具有一定的参考价值需要的小伙伴可以参考一下
    2022-05-05
  • mysql 数据库设计

    mysql 数据库设计

    大家都知道mysql的myisam表适合读操作大,写操作少;表级锁表
    2009-06-06
  • MySQL中存储过程(procedure)的使用及说明

    MySQL中存储过程(procedure)的使用及说明

    存储过程是预先定义的SQL语句集合,可在数据库中重复调用,它们提供事务性、高效性和安全性,MySQL和Java中均可创建和调用存储过程,示例展示了如何在MySQL中创建和调用存储过程,以及如何在Java中实现存储过程的调用
    2025-11-11
  • MySQL 压测实战之sysbench 从入门到精通(最新)

    MySQL 压测实战之sysbench 从入门到精通(最新)

    本文详细介绍了如何使用sysbench对MySQL进行压测,包括安装、常用压测场景、参数配置和结果解读,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • WITH在MYSQL中的用法示例详解

    WITH在MYSQL中的用法示例详解

    WITH 子句(也称为公共表表达式,Common Table Expression,简称 CTE)是 SQL 中一种强大的查询构建工具,它可以显著提高复杂查询的可读性和可维护性,这篇文章主要介绍了WITH在MYSQL中的用法,需要的朋友可以参考下
    2025-05-05
  • mysql中EXISTS和IN的使用方法比较

    mysql中EXISTS和IN的使用方法比较

    这篇文章主要给大家介绍了关于mysql中EXISTS和IN使用方法比较的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Mysql中禁用与启动触发器教程【推荐】

    Mysql中禁用与启动触发器教程【推荐】

    在使用MYSQL过程中,经常会使用到触发器,但是有时使用不当会造成一些麻烦。下面小编给大家带来了Mysql中禁用与启动触发器教程,感兴趣的朋友一起看看吧
    2018-08-08
  • Linux系统怎样查看mysql的安装路径

    Linux系统怎样查看mysql的安装路径

    这篇文章主要介绍了Linux系统怎样查看mysql的安装路径问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Centos7下mysql 8.0.15 安装配置图文教程

    Centos7下mysql 8.0.15 安装配置图文教程

    这篇文章主要为大家详细介绍了Centos7下mysql 8.0.15 安装配置图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03

最新评论