MySQL 索引失效:六大场景与原理剖析
我们都熟知索引是优化 MySQL 查询性能的利器。但你是否遇到过这样的困境:明明在表上建立了索引,查询却依然缓慢,EXPLAIN
分析后发现索引并未被使用?这就是所谓的“索引失效”。
索引失效并非一个 Bug,而是 MySQL 查询优化器 (Query Optimizer) 基于成本模型 (Cost Model) 作出的“理性”选择。它认为全表扫描 (Full Table Scan) 比使用索引的成本更低。
本文将深入数据库的底层,为你揭示索引失效背后的秘密,探讨常见的失效场景,剖析其根本原因,并提供切实可行的诊断与优化方案。
一、索引工作的核心基石:B+ 树与成本优化器
在探讨“失效”之前,我们必须先理解索引是如何“生效”的。这主要依赖于两个核心概念:
1. 数据结构:有序的 B+ 树
InnoDB 存储引擎使用 B+ 树作为索引的数据结构。其核心特点是:所有数据(或数据指针)都存储在叶子节点上,并且叶子节点之间通过双向指针连接,形成一个有序链表。
这种有序性是索引能够高效查询的关键。无论是等值查询、范围查询还是排序,MySQL 都可以利用 B+ 树的有序性,像查字典一样快速定位,避免了遍历整张表的开销。
2. 决策大脑:查询优化器 (CBO)
MySQL 使用的是基于成本的优化器 (Cost-Based Optimizer, CBO)。当一条 SQL 到达时,CBO 会生成多种可能的执行计划(例如,是走索引 A,还是索引 B,或是全表扫描),然后为每种计划估算一个执行成本,最后选择成本最低的那个去执行。
这个成本主要由两部分构成:
- I/O 成本: 从磁盘读取数据页到内存的成本。这是成本的主要部分。
- CPU 成本: 在内存中对数据进行比较、排序、计算等操作的成本。
索引失效的本质,就是 CBO 经过计算后,认为“走索引”的成本超过了“全表扫描”的成本。 我们的所有分析,都将围绕这个核心展开。
二、常见的索引失效场景及其底层原理
现在,让我们结合具体的 SQL 例子,剖析那些让 CBO“放弃”索引的典型场景。
场景 1:在索引列上进行函数或运算操作
这是最经典的失效场景。
SQL 示例:
-- `create_time` 列上有索引
-- 失效场景:对索引列使用函数
SELECT * FROM orders WHERE YEAR(create_time) = 2025;-- 失效场景:对索引列进行运算
SELECT * FROM user WHERE age + 10 = 30;
底层原因:
B+ 树中存储的是 create_time
或 age
的原始值,并且这些值是排好序的。当你在查询中对列使用了函数 YEAR()
或运算 + 10
,MySQL 无法直接使用索引中的原始值去进行匹配。
为了执行查询,MySQL 必须对表中的每一行数据都应用这个函数或运算,然后再将结果与目标值进行比较。这个过程无法利用 B+ Tee 的快速查找能力,其成本等同于全表扫描。因此,CBO 会果断放弃索引。
优化建议:
始终保持索引列的“纯净”。将计算操作移到等号右边。
-- 优化后:将函数运算移到值的身上
SELECT * FROM orders WHERE create_time BETWEEN '2025-01-01 00:00:00' AND '2025-12-31 23:59:59';SELECT * FROM user WHERE age = 20; -- 30 - 10
场景 2:LIKE 查询以通配符 %
开头
模糊查询是常见需求,但错误的用法会导致索引形同虚设。
SQL 示例:
-- `username` 列上有索引
-- 生效场景
SELECT * FROM user WHERE username LIKE 'admin%';-- 失效场景
SELECT * FROM user WHERE username LIKE '%admin';
底层原因:
再次回到 B+ 树的有序性。索引是按字母顺序排列的。
'admin%'
:前缀是确定的,CBO 可以利用索引定位到以 ‘admin’ 开头的第一个节点,然后向后顺序扫描,直到不匹配为止。这个范围是确定的,效率很高。'%admin'
:前缀是不确定的,MySQL 不知道从哪里开始查找。它无法利用 B+ 树的有序性,只能退化为全表扫描,逐一检查每个username
是否以 ‘admin’ 结尾。
优化建议:
- 尽可能避免前缀模糊查询。
- 如果业务上无法避免,可以考虑使用全文索引 (Full-Text Index) 或引入外部搜索引擎如 Elasticsearch。
场景 3:隐式类型转换
这是一个非常隐蔽的“杀手”,尤其容易在数字和字符串类型之间发生。
SQL 示例:
-- `phone` 列是 VARCHAR(20) 类型,并建有索引
-- 失效场景:传入的值是数字,导致类型不匹配
SELECT * FROM user WHERE phone = 13800138000;
底层原因:
MySQL 的规则是,当字符串和数字进行比较时,会将字符串转换为数字。因此,上述查询在 MySQL 内部实际上被转换成了:
-- MySQL 内部的隐式转换
SELECT * FROM user WHERE CAST(phone AS SIGNED) = 13800138000;
看,这又回到了场景 1 的问题——在索引列 phone
上应用了 CAST()
函数。结果自然是索引失效。
优化建议:
保证查询条件中的值类型与列定义类型完全一致。
-- 优化后:用字符串进行比较
SELECT * FROM user WHERE phone = '13800138000';
场景 4:违反复合索引的“最左前缀原则”
复合索引(或称联合索引)是提高多条件查询效率的利器,但使用不当则会失效。
SQL 示例:
-- 在 (name, age, position) 上建立复合索引
CREATE INDEX idx_name_age_pos ON employees (name, age, position);-- 生效场景
SELECT * FROM employees WHERE name = 'Tom'; -- 遵守
SELECT * FROM employees WHERE name = 'Tom' AND age = 30; -- 遵守
SELECT * FROM employees WHERE name = 'Tom' AND age = 30 AND position = 'Manager'; -- 遵守-- 失效场景
SELECT * FROM employees WHERE age = 30; -- 未从最左侧开始,索引失效
SELECT * FROM employees WHERE position = 'Manager'; -- 未从最左侧开始,索引失效
SELECT * FROM employees WHERE name = 'Tom' AND position = 'Manager'; -- 跳过了中间的 age,只有 name 部分的索引生效
底层原因:
复合索引的 B+ 树结构是“多重排序”的。以上述 (name, age, position)
索引为例,其排序规则是:
- 首先按
name
字段排序。 - 如果
name
相同,则按age
字段排序。 - 如果
age
也相同,则按position
字段排序。
当你直接查询 age = 30
时,由于 name
是不确定的,数据在 age
维度上是无序的,无法利用 B+ 树进行快速定位。必须从索引的最左列开始,并且不能跳过中间的列,索引才能被完整地利用。
优化建议:
- 严格按照复合索引的顺序设计和编写查询。
- 将最常用、选择性最高的列放在复合索引的最左侧。
场景 5:OR 条件的使用
SQL 示例:
-- `user_id` 是主键索引, `email` 是普通索引
-- 失效场景:OR 的一边没有索引
SELECT * FROM user WHERE user_id = 10 OR email = 'test@example.com';
在旧版的 MySQL 中,OR
常常导致索引失效。但随着版本迭代,MySQL 引入了 索引合并 (Index Merge) 优化。如果 OR
两边的条件列都有索引,优化器可能会分别使用两个索引,然后将结果集合并。
但索引仍然可能失效,通常是因为:
- OR 的其中一个条件列没有索引:此时优化器无法对整个
OR
查询进行有效的索引操作,它会认为全表扫描后用user_id = 10 OR email = '...'
进行过滤的成本更低。 - 优化器认为全表扫描更快:即使两边都有索引,如果优化器估算
OR
条件会返回大量数据(例如,status = 'active' OR age > 20
),它可能会判断全表扫描比索引合并的成本(两次索引扫描 + 结果去重合并)更低。
优化建议:
- 确保
OR
两边的列都有索引。 - 如果业务允许,可以考虑将
OR
查询拆分成两个独立的查询,用UNION ALL
合并。SELECT * FROM user WHERE user_id = 10 UNION ALL SELECT * FROM user WHERE email = 'test@example.com' AND user_id != 10;
场景 6:范围查询或不等式(!=
, <>
)
SQL 示例:
-- `age` 列有索引
SELECT * FROM user WHERE age > 20;-- `status` 列有索引
SELECT * FROM user WHERE status != 'active';
底层原因:
这不是绝对的失效,而是“可能”失效,根本原因在于回表成本和选择性 (Selectivity)。
- 选择性:指索引列中不同值的比例。如果一个索引的选择性很差(例如
status
列只有 ‘active’ 和 ‘inactive’ 两个值),那么status != 'active'
几乎会返回一半的数据。 - 回表成本:对于非覆盖索引,通过索引找到主键后,还需要根据主键去聚簇索引中查找完整的行数据,这个过程叫回表。
当优化器估算,一个范围查询或不等式查询需要扫描大量的索引条目,并且每次扫描后还需要进行大量的回表操作时,它会认为这个 I/O 成本总和超过了直接全表扫描的成本。全表扫描是一次顺序 I/O,而大量的回表是随机 I/O,后者通常更昂贵。
优化建议:
- 尽量避免使用
!=
或<>
。 - 对于选择性差的列,不适合单独建立索引。
- 对于频繁的范围查询,可以考虑使用覆盖索引(查询的所有列都包含在索引中),以避免回表,从而大大降低成本。
三、诊断与优化:让索引“起死回生”
1. 神器 EXPLAIN
EXPLAIN
是诊断索引问题的首要工具。将它放在你的 SELECT
语句前执行,可以查看 MySQL 的执行计划。
EXPLAIN SELECT * FROM user WHERE age = 30;
重点关注以下几列:
type
: 连接类型。ALL
代表全表扫描,是性能最差的情况。理想值是const
,eq_ref
,ref
,range
等。possible_keys
: 可能使用的索引。key
: 实际使用的索引。如果为NULL
,则表示索引失效。rows
: 估算需要扫描的行数。数值越小越好。Extra
: 额外信息。Using filesort
(需要额外排序)、Using temporary
(使用了临时表)都是危险信号。Using index
是个好信号,表示使用了覆盖索引。
2. 更新统计信息
优化器依赖表的统计信息(如行数、索引的基数等)来做决策。如果数据表发生大量增删改,统计信息可能过时,导致优化器做出错误判断。
可以手动更新统计信息:
ANALYZE TABLE your_table_name;
3. 强制索引(谨慎使用)
如果你确信优化器的选择是错误的,可以使用 FORCE INDEX
来强制它使用某个索引。
SELECT * FROM employees FORCE INDEX (idx_name_age_pos) WHERE age = 30;
⚠️ 警告:这通常是最后的手段。它绕过了优化器的智能判断,可能在数据分布变化后导致性能问题。首选应该是优化查询或索引设计。
四、总结
索引失效并非玄学,而是 MySQL 查询优化器基于 B+ 树结构、数据统计信息和成本模型进行权衡后的理性选择。
为了让你的索引持续高效工作,你需要像优化器一样思考:
- 保持索引列的纯粹:避免函数、运算和隐式转换。
- 遵循索引的结构:利用好最左前缀原则和索引的有序性。
- 降低回表成本:善用覆盖索引。
- 相信但要验证:以
EXPLAIN
的结果为准绳,诊断并指导优化。
希望这篇博客可以帮助你理解索引失效的场景和原理。
相关文章:
MySQL 索引失效:六大场景与原理剖析
我们都熟知索引是优化 MySQL 查询性能的利器。但你是否遇到过这样的困境:明明在表上建立了索引,查询却依然缓慢,EXPLAIN 分析后发现索引并未被使用?这就是所谓的“索引失效”。 索引失效并非一个 Bug,而是 MySQL 查询…...

打造你的 Android 图像编辑器:深入解析 PhotoEditor 开源库
📸 什么是 PhotoEditor? PhotoEditor 是一个专为 Android 平台设计的开源图像编辑库,旨在为开发者提供简单易用的图像编辑功能。它支持绘图、添加文本、应用滤镜、插入表情符号和贴纸等功能,类似于 Instagram 的编辑体验。该库采…...
DeepSeek 终章:破局之路,未来已来
目录 一、DeepSeek 技术发展现状回顾二、未来发展趋势2.1 多模态融合的拓展2.2 模型可解释性的强化2.3 垂直领域的深化应用 三、面临的技术挑战3.1 数据隐私与安全难题3.2 算法偏见与公平性困境3.3 网络攻击与恶意利用威胁 四、挑战应对策略探讨4.1 技术层面的解决方案4.2 算法…...
八:操作系统设备管理之缓冲、缓存与假脱机
弥合鸿沟:操作系统中的缓冲、缓存与假脱机技术深度解析 在计算机系统的世界里,存在着一个根本性的速度差异:中央处理器(CPU)的执行速度飞快,而输入/输出(I/O)设备(如硬盘…...

Azure 虚拟机端口资源:专用 IP 和公共 IP Azure Machine Learning 计算实例BUG
## 报错无解 找不到Azure ML 计算实例关联的 NSG .env 文件和 ufw status: .env 文件中 EXPOSE_NGINX_PORT8080 是正确的,它告诉 docker-compose.yaml 将 Nginx 暴露在宿主机的 8080 端口。 sudo ufw status 显示 Status: inactive,意味着宿…...

Java核心技术-卷I-读书笔记(第十二版)
第一章 Java程序设计概述 09年sun被oracle收购->11年java7(简单改进)->14年java8(函数式编程)->2017年java9->2018年java11->2021年java17 第二章 Java编程环境 Java9后新增JShell,提供类似脚本试执…...
从C到C++语法过度1
从C到C语法过度1 文章目录 从C到C语法过度11. 字符串string2. 引用3. 类型转换3.1 新式转换 const_cast3.2 新式转换 static_cast 4. 关键字auto 1. 字符串string C语言从本质上来说,是没有字符串这种类型的,在C语言中如果要表达字符串,只能…...
AI是如何换装的?
AI换装是一种基于计算机视觉、深度学习和生成对抗网络(GAN)的技术,能够通过算法自动识别人像并更换服饰,实现虚拟换装的效果。这项技术广泛应用于电商服装试穿、虚拟偶像、影视特效、社交媒体滤镜等领域。 AI换装的核心技术 1. 图像分割与人体解析 换装的第一步是图像分…...

MATLAB遍历生成20到1000个节点的无线通信网络拓扑推理数据
功能: 遍历生成20到1000个节点的无线通信网络拓扑推理数据,包括网络拓扑和每个节点发射的电磁信号,采样率1MHz/3000,信号时长5.7s,单帧数据波形为实采 数据生成效果: 拓扑及空间位置: 节点电磁…...
python爬虫:grequests的详细使用(基于gevent和requests的异步HTTP请求库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、grequests 概述1.1 grequests 介绍1.2 注意事项1.3 替代方案比较1.4 基本组件1.5 grequests 安装二、基本用法2.1 创建请求任务2.2 发送请求并获取响应2.3 带参数的请求三、高级用法3.1 自定义回调函数3.2 设置超时…...
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
在使用PHP连接MySQL数据库时,可能会遇到连接失败和写锁问题。这类问题可能会影响应用的正常运行,本文将详细介绍排查和解决这些问题的方法。 一、PHP连接MySQL数据库失败 1. 排查连接失败的常见原因 数据库配置错误: 检查数据库主机、用户名…...

卫星接收天线G/T值怎么计算?附G/T计算excel表格链接
我们在进行无线通信链路设计时,都会涉及接收天线最重要的参数G/T。今天,咱们就来聊聊G/T值该怎么计算,计算过程中有哪些需要留意的地方,以及当你看到产品说明书中标注了G/T指标,还需要进一步了解哪些信息。 G/T的含义 …...

基于dify的营养分析工作流:3分钟生成个人营养分析报告
你去医院做体检,需要多久拿到体检报告呢?医院会为每位病人做一份多维度的健康报告吗?"人工报告需1小时/份?数据误差率高达35%?传统工具无法个性化? Dify工作流AI模型的组合拳,正在重塑健康…...
Kali Linux 安全工具解析
Kali Linux 安全工具解析 目录 Kali Linux 安全工具解析一、Kali Linux 概述1.1 核心特性1.2 系统要求 二、核心工具分类与实战应用2.1 信息收集工具2.1.1 Nmap2.1.2 Recon-ng2.1.3 theHarvester 2.2 漏洞利用工具2.2.1 Metasploit Framework2.2.2 SQLMap2.2.3 Burp Suite 2.3 …...
端午编程小游戏--艾草驱邪
刚刚过去的端午,参加了学校的一个活动,用python做了一个小游戏,当然这个小游戏还可以继续改进,可以加个bgm什么的...... 可以小玩一下 import pygame import random import math import sys import timepygame.init() pygame.mi…...

新成果:GaN基VCSEL动态物理模型开发
作为高速数据传输与光电信号处理的核心器件,垂直腔面发射激光器(VCSEL)在高速光通信、激光雷达等领域应用广泛,其动态特性直接关联器件调制速率及稳定性等关键参数。近期,天津赛米卡尔科技有限公司技术团队开发了GaN基…...
0x-4-Oracle 23 ai-sqlcl 25.1.1 独立安装-配置和优化
一、独立安装sqlcl 1. 安装 Java 环境 SQLcl 需要 Java 1.8.0_220 或更高版本, Oracle Linux9.6 上已经默认安装Oracle 23ai后Java 是11 lts版本 如果java jdk安装错误将遇上SQLcl困扰n多人的bug sql /nolog 错误:找不到或加载主类 oracle.dbto…...

Appium+python自动化(十一)- 元素定位- 下
1、 List定位 List顾名思义就是一个列表,在python里面也有list这一个说法,如果你不是很理解什么是list,这里暂且理解为一个数组或者说一个集合。首先一个list是一个集合,那么他的个数也就成了不确定性,所以这里需要用复…...

免费批量PDF转Word工具
免费批量PDF转Word工具 工具简介 这是一款简单易用的批量PDF转Word工具,支持: 批量转换多个PDF文件保留原始格式和布局快速高效的转换速度完全免费使用 工具地址 下载链接 网盘下载地址:点击下载 提取码:8888 功能特点 ✅…...
OD 算法题 B卷【水果摊小买卖】
文章目录 水果摊小买卖 水果摊小买卖 小王手里有点闲钱,想做点水果买卖,给出两个数组m, n, m[i]表示第i个水果的成本价,n[i]表示第i个水果能卖出的价格;假如现在有本钱k,试问最后最多能赚多少钱࿱…...

Mac/iOS 如何解压 RAR 格式压缩包:常用工具与详细操作步骤
一、Mac 系统解压 RAR 文件之法 Mac 系统上解压 RAR 文件有多种方法,除了系统自带的一些简单功能外,还可以借助特定的软件来实现高效解压。以下将介绍几款常用工具的解压操作。 (一)解压专家解压步骤 解压专家 是一款在 Mac 和 …...
二进制安全-IDA Pro-API
idaapi 是 IDA Pro(Interactive Disassembler Professional) 反汇编工具的 Python API 接口,用于开发自动化脚本、插件和自定义分析工具。通过 idaapi,开发者可以访问 IDA Pro 的核心功能(如反汇编、符号分析、交叉引用…...

机器学习监督学习实战四:九种回归算法对波士顿房价数据进行回归预测和评估方法可视化
本项目代码在个人github链接:https://github.com/KLWU07/Machine-learning-Project-practice/tree/main 处理流程 1.导入波士顿房价数据集并进行预处理。2.使用 GradientBoostingRegressor 模型进行回归分析。3.通过交叉验证评估模型的性能,计算 MAE、…...
1. Web网络基础 - IP地址核心知识解析
深入解析IP地址与ipconfig命令:网络工程师的必备技能 在网络世界中,IP地址是设备通信的基石。本文将全面解析IP地址的核心概念,并通过ipconfig命令实战演示如何获取关键网络配置信息。 一、IP地址核心知识解析 1. IP地址的本质 定义&#x…...

微软重磅发布Magentic UI,交互式AI Agent助手实测!
微软重磅发布Magentic UI,交互式AI Agent助手实测! 何为Magentic UI? Magentic UI 是微软于5.19重磅发布的开源Agent助手,并于24日刚更新了第二个版本0.04版 从官方的介绍来看,目标是打造一款 以人为中心 的智能助手,其底层由多个不同的智能体系统驱动,能够实现网页浏览…...
c# 完成恩尼格玛加密扩展
c# 完成恩尼格玛加密扩展 恩尼格玛扩展为可见字符恩尼格玛的设备原始字符顺序转子的设置反射器的设置连接板的设置 初始数据的设置第一版 C# 代码第二版 C# 代码 总结 恩尼格玛 在之前,我们使用 python 实现了一版恩尼格玛的加密算法,但是这一版&#x…...
华为 “一底双长焦” 专利公布,引领移动影像新变革
6 月 6 日,国家知识产权局公布的一项专利发明申请吸引了众多目光,该专利发明人为华为技术有限公司,名为 “光学镜头、摄像头模组及电子设备” 。从展示的技术图来看,这一光学镜头呈现出独特的 “一底双镜头结构”,其中…...

老年生活照护实训室建设规划:照护质量评估与持续改进实训体系
随着人口老龄化程度的不断加深,老年生活照护需求日益增长,对专业照护人才的培养提出了更高要求。老年生活照护实训室建设方案作为培养高素质照护人才的重要载体,其核心在于构建科学完善的照护质量评估与持续改进实训体系。通过该体系的建设&a…...

【python深度学习】Day 48 PyTorch基本数据类型与操作
知识点: 随机张量的生成:torch.randn函数卷积和池化的计算公式(可以不掌握,模型会自动计算的)pytorch的广播机制:加法和乘法的广播机制 ps:numpy运算也有类似的广播机制,基本一致 作…...
Go深入学习延迟语句
1 延迟语句是什么 编程的时候,经常会需要申请一些资源,比如数据库连接、文件、锁等,这些资源需要再使用后释放掉,否则会造成内存泄露。但是编程人员经常容易忘记释放这些资源,从而造成一些事故。 Go 语言直接在语言层…...