为什么MySQL索引不生效?来看看这8个原因
wptr33 2025-10-14 06:17 3 浏览
在数据库优化中,最让人头疼的事情之一莫过于精心设计的索引没有发挥作用。为什么会出现这种情况?
这篇文章带大家一起探讨一些常见原因,方便大家更好地理解 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_name 和 state 字段:
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:表扫描更快
某些情况下,优化器会选择直接扫描整个表而不是使用索引。这可能发生在以下场景:
- 表的大小较小,表扫描成本几乎可以忽略。
- 查询需要获取大部分表数据,索引的过滤能力不足,导致索引的额外开销反而拖慢查询速度。
虽然表扫描看起来反直觉,但在特定情况下确实更高效。
原因 6:索引的结构性限制
理解索引的底层结构(如 B+ 树),有助于分析某些查询为什么无法用到索引。主要有以下几个场景:
场景 1:通配符搜索
MySQL 的索引只能用于匹配字符串的前缀部分,不能用于字符串中的后缀或包含部分。例如:
- 查询 LIKE 'Aa%' 可以使用索引。
- 查询 LIKE '%ron' 则无法使用索引。
如果你需要复杂的字符串搜索,可以考虑使用全文索引(Fulltext Index)或专门设计的数据模型。
场景 2:复合索引的左前缀规则
复合索引要求使用时遵循“左前缀”规则,例如:
ALTER TABLE people ADD INDEX multi (first_name, state);
- 查询条件包含 first_name 和 state 时会正确使用索引。
- 查询条件仅包含 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 应该谨慎,因为可能在数据量增长后需要重新评估是否强制使用某索引。
总结
索引优化涉及多个方面,包括查询优化器运作、数据分布、索引结构等。了解索引未生效的原因并合理优化查询,可以显著提升数据库性能。索引虽强大,但只有正确规划和使用才能发挥最大效用。
相关推荐
- 深度剖析 MySQL 数据库索引失效场景与优化策略
-
在互联网软件开发领域,MySQL数据库凭借其开源、高效等特性被广泛应用。而索引,作为提升MySQL查询性能的关键利器,能大幅加速数据检索。然而,在实际开发中,即便精心创建了索引,却常常遭遇索引失...
- 15分钟,带你了解indexedDB,这个前端存储方案很重要!
-
原文来源于:程序员成长指北;作者:Django强哥如有侵权,联系删除最近在给前端班授课,在这次之前的最后一次课已经是在2年前,2年的时间,前端的变化很大,也是时候要更新课件了。整理客户端存储篇章时模糊...
- MySQL 面试总被问到的那些问题,你都懂了吗?
-
事务的四大特性是什么?首先得提一下ACID,这可是数据库事务的灵魂所在:原子性(Atomicity):要么全部成功,要么全部失败回滚。一致性(Consistency):确保数据在事务前后都处于一致状态...
- Java 字符串常见的操作_java字符串总结
-
在Java当中,为字符串类提供了丰富的操作方法,对于字符串,我们常见的操作就是:字符串的比较、查找、替换、拆分、截取以及其他的一些操作。在Java中,有String,StringBuffer和St...
- java学习分享:Java截取(提取)子字符串(substring())
-
在String中提供了两个截取字符串的方法,一个是从指定位置截取到字符串结尾,另一个是截取指定范围的内容。下面对这两种方法分别进行介绍。1.substring(intbeginIndex)形...
- 你必须知道的 7 个杀手级 JavaScript 单行代码
-
1.如果你需要一个临时的唯一ID,请生成随机字符串。这个例子将为你生成一个随机字符串:constrandomString=Math.random().toString(36).slice(2)...
- MySQL 索引失效:原因、场景与解决方案
-
在互联网软件开发领域,MySQL作为一款广泛使用的关系型数据库,其性能优化至关重要。而索引,作为提升MySQL查询性能的关键手段,一旦失效,会导致查询效率大幅下降,影响整个系统的性能。今天,就来...
- Axure9 教程:可模糊搜索的多选效果
-
一、交互效果说明1.点击话题列表中的话题选项,上方输入框内显示选择的话题标签,最多可选择5个标签,超出将有文字提示。2.点击输入框内已选择的话题标签的删除按钮,可以删除已选择的话题标签,并且该标签返回...
- JavaScript字符串操作方法大全,包含ES6方法
-
一、charAt()返回在指定位置的字符。...
- 为什么MySQL索引不生效?来看看这8个原因
-
在数据库优化中,最让人头疼的事情之一莫过于精心设计的索引没有发挥作用。为什么会出现这种情况?这篇文章带大家一起探讨一些常见原因,方便大家更好地理解MySQL查询优化器是如何选择索引的,以及在出现类...
- Kettle实现rabbitMQ的生产与消费_rabbitmq不支持顺序消费
-
文章目录一、Kettle为什么可以读取流数据?...
- MySQL高频函数Top10!数据分析效率翻倍,拒绝无效加班!
-
引言:为什么你的SQL代码又臭又长?“同事3行代码搞定的事,你写了30行?”“每次处理日期、字符串都抓狂,疯狂百度?”——不是你不努力,而是没掌握这些高频函数!本文精炼8年数据库开发经验,总结出10个...
- mysql的截取函数用法详解_mysql截取指定字符
-
substring()函数测试数据准备:用法:以下语法是mysql自动提示的1:substirng(str,pos):从指定位置开始截取一直到数据完成str:需要截取的字段的pos:开始截取的位置。从...
- MySQL函数:字符串如何截取_mysql 字符串截取函数
-
练习截取字符串函数(五个)mysql索引从1开始...
- 数据集成产品分析(一)_数据集成工具有哪些
-
编辑导语:数据集成产品是数据中台建设的第一环节,在构建数据中台或大数据系统时,首先要将企业内部各个业务系统的数据实现互联互通,从物理上打破数据孤岛。本文作者对数据集成产品进行了分析,一起来看一下吧。数...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
如何将AI助手接入微信(打开ai手机助手)
-
SparkSQL——DataFrame的创建与使用
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
Java面试必考问题:什么是乐观锁与悲观锁
-
redission YYDS spring boot redission 使用
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)