百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

为什么MySQL索引不生效?来看看这8个原因

wptr33 2025-10-14 06:17 25 浏览

在数据库优化中,最让人头疼的事情之一莫过于精心设计的索引没有发挥作用。为什么会出现这种情况?

这篇文章带大家一起探讨一些常见原因,方便大家更好地理解 MySQL 查询优化器是如何选择索引的,以及在出现类似问题时,可逐项进行对照排查,

以一个简单的 people 表作为例子,表结构如下:

CREATE TABLE `people` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) NOT NULL,
  `last_name` varchar(50) NOT NULL,
  `state` char(2) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `first_name` (`first_name`),
  KEY `state` (`state`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 #技术分享 COLLATE=utf8mb4_unicode_ci

后续会以该表结构为基础,通过添加或删除索引来展示不同场景。

确认索引是否被使用

在分析索引未生效的原因之前,首先需要判断 MySQL 是否使用了索引。可以通过 EXPLAIN 命令来查看查询优化器的分析结果,了解哪些索引被考虑,以及最终选择使用了哪个索引。

例如,以下查询会试图通过 first_name 索引查找数据:

EXPLAIN SELECT * FROM people WHERE first_name = 'Aaron'

返回结果如下:

| id | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | | ---

| 1 | people | ref | first_name | first_name | 202 | const | 180 | 100.00 | |

从结果中可以看到:

  • possible_keys 表示查询优化器会考虑的索引,这里是 first_name 索引。
  • key 表示实际被选中的索引,也是 first_name

关于 EXPLAIN 的使用,可参考《 仅仅会用 MySQL 的 EXPLAIN 还不够,还需要会用 EXPLAIN ANALYZE 》这篇文章。

在本例中,first_name 索引不仅被优化器考虑(considered),而且最终被选中(chosen)。这是两个相关但不同的步骤:首先,优化器会根据查询筛选可用的索引;然后,选择性能较优的索引。

确认索引是否被使用后,接下来分析一些索引未生效的常见原因。

索引未生效的原因

原因 1:另一个索引更优

当查询可以利用多个索引时,MySQL 优化器会选择其中最优的索引。如果你的查询可以同时使用多个索引,但最终未选择预期的索引,很可能是因为另一个索引的效率更好。

例如,以下查询同时使用 first_namestate 字段:

SELECT *

WHERE first_name = 'Aaron' AND state = 'TX'

运行 EXPLAIN 后结果如下:

| id | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | | ---

| 1 | people | ref | first_name,state | first_name | 202 | const | 180 | 50.00 | Using where |

在这个例子中,first_name 索引比 state 索引的选择性更高,因此优化器选择了 first_name 索引。

原因 2:索引的选择性和基数

索引的性能往往与选择性和基数相关:

  • 基数(Cardinality) 是列中不同值的数量。
  • 选择性(Selectivity) 是指这些值的独特程度(计算公式为 COUNT(DISTINCT column) / COUNT(*) )。

比如,可以通过以下查询计算基数和选择性:

SELECT
  COUNT(DISTINCT first_name) as first_name_cardinality,
  COUNT(DISTINCT state) as state_cardinality,
  COUNT(DISTINCT first_name) / COUNT(*) as first_name_selectivity,
  COUNT(DISTINCT state) / COUNT(*) as state_selectivity
FROM people;

结果如下:

| first_name_cardinality | state_cardinality | first_name_selectivity | state_selectivity | | ---

| 3009 | 2 | 0.0060 | 0.0000 |

  • first_name 字段的不同值非常多,因此选择性较高。
  • state 列选择性极低,导致通过 state 索引进行过滤时,效果较差。

高选择性索引通常性能较优,而低选择性索引在过滤数据时作用有限。

此外,唯一索引(如 id 的主键索引)通常具有完美选择性。

原因 3:选择性因查询而异

索引的选择性是基于整个表数据分布进行计算的,但选择性在具体查询场景中可能不一样。例如:

假如表中有 100 万行,其中 99% 的用户类型是 user ,只有 1% 为 admin ,总体来看 type 列选择性很低。但如果你的查询条件是 type = 'admin' ,此时索引的作用就很明显。

优化器会根据查询条件和数据分布动态评估索引的价值。

原因 4:过时或不准确的统计数据

MySQL 的索引基数统计信息是通过随机采样维护的,可能出现因统计信息过时而导致优化器做出错误决策的情况。可以通过以下命令更新统计信息:

ANALYZE TABLE people;

如果统计数据采样精度不足,可以通过调整 MySQL 的相关参数改善采样质量。

原因 5:表扫描更快

某些情况下,优化器会选择直接扫描整个表而不是使用索引。这可能发生在以下场景:

  1. 表的大小较小,表扫描成本几乎可以忽略。
  2. 查询需要获取大部分表数据,索引的过滤能力不足,导致索引的额外开销反而拖慢查询速度。

虽然表扫描看起来反直觉,但在特定情况下确实更高效。

原因 6:索引的结构性限制

理解索引的底层结构(如 B+ 树),有助于分析某些查询为什么无法用到索引。主要有以下几个场景:

场景 1:通配符搜索

MySQL 的索引只能用于匹配字符串的前缀部分,不能用于字符串中的后缀或包含部分。例如:

  • 查询 LIKE 'Aa%' 可以使用索引。
  • 查询 LIKE '%ron' 则无法使用索引。

如果你需要复杂的字符串搜索,可以考虑使用全文索引(Fulltext Index)或专门设计的数据模型。

场景 2:复合索引的左前缀规则

复合索引要求使用时遵循“左前缀”规则,例如:

ALTER TABLE people ADD INDEX multi (first_name, state);
  • 查询条件包含 first_namestate 时会正确使用索引。
  • 查询条件仅包含 state 时因不满足左前缀无法使用复合索引。

场景 3:连接列类型或字符集不匹配

若连接的字段类型或字符集不一致,索引将无法生效。例如:

  • VARCHAR(10)CHAR(10) 类型和长度相同,索引可用。
  • VARCHAR(10)CHAR(15) 则因长度不同导致索引不可用。

确保字段定义一致是索引生效的前提。

原因 7:索引被模糊化处理

某些查询因对字段使用了函数或运算导致索引无法使用。例如:

SELECT * FROM people WHERE YEAR(created_at) = 2023;

上述查询无法使用 created_at 索引,因为 MySQL 没法直接基于函数计算进行优化。替代方案如下:

SELECT * FROM people WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';

通过范围查询可以正常使用索引。

原因 8:隐藏索引

MySQL 支持隐藏索引,隐藏索引不会被查询优化器使用。例如:

ALTER TABLE people ALTER INDEX first_name INVISIBLE;

Hidden 索引可以用于测试索引删除的影响,若查询性能下降可以随时恢复索引。

强制使用索引

如果你认为 MySQL 优化器的决策不正确,可以通过 USE INDEX 提示优化器使用指定索引:

EXPLAIN SELECT * FROM people USE INDEX (state) WHERE first_name = 'Aaron' AND state = 'TX'

但使用 USE INDEX 应该谨慎,因为可能在数据量增长后需要重新评估是否强制使用某索引。

总结

索引优化涉及多个方面,包括查询优化器运作、数据分布、索引结构等。了解索引未生效的原因并合理优化查询,可以显著提升数据库性能。索引虽强大,但只有正确规划和使用才能发挥最大效用。

相关推荐

oracle数据导入导出_oracle数据导入导出工具

关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...

继续学习Python中的while true/break语句

上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个else解...

python continue和break的区别_python中break语句和continue语句的区别

python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...

简单学Python——关键字6——break和continue

Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...

2-1,0基础学Python之 break退出循环、 continue继续循环 多重循

用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...

Python 中 break 和 continue 傻傻分不清

大家好啊,我是大田。今天分享一下break和continue在代码中的执行效果是什么,进一步区分出二者的区别。一、continue例1:当小明3岁时不打印年龄,其余年龄正常循环打印。可以看...

python中的流程控制语句:continue、break 和 return使用方法

Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...

L017:continue和break - 教程文案

continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...

作为前端开发者,你都经历过怎样的面试?

已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...

面试被问 const 是否不可变?这样回答才显功底

作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...

前端面试总结_前端面试题整理

记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...

由浅入深,66条JavaScript面试知识点(七)

作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录由浅入深,66条JavaScript面试知识点(一)由浅入深,66...

2024前端面试真题之—VUE篇_前端面试题vue2020及答案

添加图片注释,不超过140字(可选)1.vue的生命周期有哪些及每个生命周期做了什么?beforeCreate是newVue()之后触发的第一个钩子,在当前阶段data、methods、com...

今年最常见的前端面试题,你会做几道?

在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...