目录
背景
Where条件查询执行过程
Index Key
Index Filter
Table Filter
总结和补充
分析索引失效原因
范围查询导致联合索引停止匹配
回表操作的开销
总结
优化索引
拓展
索引失效常见场景
Explain分析
首页 数据库 mysql教程 Mysql索引失效怎么办?失效原因浅析

Mysql索引失效怎么办?失效原因浅析

Nov 02, 2021 am 11:28 AM
mysql 索引

本篇文章给大家记录一次Mysql索引失效,分析一下Mysql索引失效原因,希望对大家有所帮助!

Mysql索引失效怎么办?失效原因浅析

此文章包含Mysql的Where条件查询执行过程、范围查询使联合索引停止匹配、回表操作分析、常见索引失效场景、Extra分析等知识。【相关推荐:mysql视频教程

背景

6千万数据量的数据表出现了一个满查询,复现sql语句发现查询并没有走索引而是走全表查询,找出索引失效原因。

# sql语句
EXPLAIN SELECT count(*) FROM order_recipient_extend_tab WHERE start_date>&#39;1628442000&#39; and start_date<&#39;1631120399&#39; and station_id=&#39;1809&#39; and status=&#39;2&#39;;

1.png

order_recipient_extend_tab 表有6千万数据,慢查询的查询字段包括 start_date、station_id、status,按照索引设计初衷会走但实际上失效的索引是:

联合索引 字段1 字段2 字段3
idx_date_station_driver start_date station_id driver_id

Where条件查询执行过程

了解Mysql怎么执行where条件查询,能更快速清晰地洞见索引失效的原因。此次慢查询中匹配度高的索引是idx_date_station_driver,分析此次慢查询中where条件查询的执行过程。

Mysql对where条件提取规则主要可以归纳为三大类:Index Key (First Key & Last Key),Index FilterTable Filter

Index Key

Index Key用于确定此次sql查询在索引树上的范围。一个范围包括起始和终止,Index First Key用于定位索引查询的起始范围,Index Last Key用于定位索引查询的终止范围

  • Index First Key

    提取规则:从索引的第一个字段开始,检查该字段在where条件中是否存在,若存在且条件是=、>=,则将对应的条件加入Index First Key之中,继续读取索引的下一个字段;若存在且条件是>,则将对应的条件加入Index First Key中,然后终止Index First Key的提取;若不存在,也终止Index First Key的提取。

  • Index Last Key

    与Index First Key正好相反,提取规则:从索引的第一个字段开始,检查其在where条件中是否存在,若存在并且条件是=、<=,则将对应条件加入到Index Last Key中,继续提取索引的下一个字段;若存在并且条件是 < ,则将条件加入到Index Last Key中,然后终止提取;若不存在,也终止Index Last Key的提取。

按照Index Key的提取规则,在此次慢查询中提取出来的Index Last Key为:start_date>'1628442000',Index Last Key为: start_date<'1631120399'。

Index First Key只是用来定位索引的起始范围,使用Index First Key条件,从索引B+树的根节点开始,使用二分搜索方法快速索引到正确的叶节点位置。Where查询过程中Index First Key只做了一次判断。

Index Last Key,用来定位索引的终止范围,因此对于起始范围之后读到的每一条索引记录,均需要判断是否已经超过了Index Last Key的范围,若超过,则当前查询结束。

Index Filter

在Index Key确定的索引范围中,并不是所有的索引记录都满足查询条件。比如Index Last Key和Index Last Key范围中,不是所有索引记录都满足 station_id = '1809'。这个时候就需要用到Index Filter了。

Index Filter,又名索引下推用于过滤索引查询范围中不满足查询条件的记录。对于索引范围中的每一条记录,均需要与Index Filter进行对比,若不满足Index Filter则直接丢弃,继续读取索引下一条记录。

Index Filter的提取规则:从索引的第一个字段开始,检查其在where条件中是否存在,若存在且条件仅为 =,则跳过第一字段继续检查索引下一字段,下一索引列采取相同的提取规则(解释:条件为=的字段已经在Index Key中过滤掉了);若存在且条件为 >=、>、<、<= 其中的几种,则跳过当前索引字段,将其余where条件中索引相关字段全部加入到Index Filter之中。

按照Index Filter的提取规则,在此次慢查询中提取出来的Index Filter为:station_id='1809'。在Index Key确定的索引查询范围中,遍历索引记录时都需要比较 station_id='1809',不满足该条件则直接丢失,继续读取索引下一条记录。

Table Filter

Table Filter用于过滤掉索引无法过滤的数据。在二级索引中通过主键回表查询到整行记录后,判断该记录是否符合Table Filter条件,不符合则丢失,继续判断下一条记录。

提取规则很简单:所有不属于索引字段的查询条件,均归为Table Filter之中。按照Table Filter的提取规则,在此次查询中Table Filter为:status=‘2’。

总结和补充

Index Key用于确定索引扫描的范围;Index Filter用于在索引中进行过滤;Table Filter需要回表后在Mysql服务器进行过滤。

Index Key和Index Filter发生在InnoDB存储层,Table Filter发生在Mysql Server层。

在 MySQL5.6 之前,并不区分Index Filter与Table Filter,统统将Index First Key与Index Last Key范围内的索引记录,回表读取完整记录,然后返回给MySQL Server层进行过滤。

在MySQL 5.6及之后,Index Filter与Table Filter分离,Index Filter下降到InnoDB的存储引擎层进行过滤,减少了回表与返回MySQL Server层的记录交互开销,提高了SQL的执行效率。

分析索引失效原因

首先是count(),此时通配符 * 经优化并不会拓展所有列,实际上会忽略所有的列直接统计行数。所以只想收集行数最好使用count()。

接下来分析where语句。假设此慢查询会使用了二级索引idx_date_station_driver,按照上面where条件查询的执行过程,该慢查询的Index First Key为start_date>'1628442000',Index Last Key为: start_date<'1631120399',Index Filter为:station_id='1809',Table Filter为:status=‘2’。

提取Index First Key后在索引B+树上定位索引起始范围就是索引匹配的过程,在索引B+树上使用二分搜索方法快速定位符合查询条件的起始叶子节点。通过上文Where条件查询执行过程,我们知道该慢查询的where条件(start_date>'1628442000' and start_date<&#39;1631120399&#39; and status=&#39;2&#39; and station_id=&#39;1809&#39;),只匹配了索引idx_date_station_driver(start_date, station_id, driver_id)的第一个字段,即只匹配了idx_date_station_driver(start_date),station_id='1809‘精确查询并没有作用到匹配索引上,而是在Index Filter即索引下推过程中发挥了作用。实际上这里是因为范围查询使联合索引停止匹配

范围查询导致联合索引停止匹配

为什么范围查询会使联合索引停止匹配?这里涉及到最左前缀匹配原理。假设建立一个联合索引 index(a, b),会先对a进行排序,在a相等的情况下对b进行排序,如下图所示。在该索引树上,a是全局有序的,而b则处于全局无序、局部有序状态。从全局来看,b的值为1、2、1、4、1、2,只有 b=2 查询条件无法直接使用该索引;从局部来看,当a的值确定时,b则是有序状态,a=2 && b=4可以使用该索引。所以范围查询使联合索引停止匹配的根本原因是,索引树上非首字段的有序状态依赖前一个字段相等情况,而范围查询破坏了下一个索引字段局部有序状态,导致索引停止匹配。

2.png

范围查询使联合索引停止匹配,并不能在索引匹配的时候就过滤掉 station_id不等于'1809' 的数据,导致Mysql在索引上的扫描范围Index First Key和Index Last Key完全由start_timestamp_of_date时间决定。start_timestamp_of_date范围查询可以过滤73%数据量,而station_id='1809'精确查询能过滤掉99%的数据量。

查询条件数据量占比
所有数据6367万100%
start_timestamp_of_date>'1628442000' and start_timestamp_of_date<'1631120399'1742万27.35%
station_id='1809'8万0.16%

回表操作的开销

由于status字段不在索引idx_date_station_driver字段上,所以需要回表查询索引过滤的数据,在Mysql服务层判数据是否符合查询条件。

Mysql的优化器在执行sql语句时会先估算走匹配度高的索引的开销,如果走索引的开销比查全表还大,那么Mysql会选择全表扫描。这个结论可能反常识,在我们印象中索引就是用来提高查询效率的。这里主要涉及两个因素:

  • 当查询条件或查找的字段不在二级索引的字段上时,会执行回表操作,会走:二级索引+主键索引。

  • 磁盘随机I/O的性能低于顺序I/O。回表查询在主键索引上是随机I/O,全表扫描在主键索引上是顺序I/O。

做实验分析回表操作的开销是否是索引失效的直接原因?

去除status='0'查询条件,explain查看该查询是否使用到了索引idx_date_station_driver。结果如下图所示,少了回表操作的开销,索引并未失效。

3.png

总结

结合以上分析总结索引失效原因是:范围查询使联合索引停止匹配,索引匹配过滤的数据不够多,导致Mysql优化器估算出Table Filter的回表操作开销大于全表查询,所以选择了全表查询。范围查询使联合索引停止匹配是索引失效的罪魁祸首,而回表操作的开销是索引失效的直接原因。

优化索引

该慢查询索引失效的罪魁祸首是范围查询使联合索引停止匹配,只需要把范围查询的字段调整到精确查询的字段后面,即将

联合索引 idx_date_station_driver(start_date, station_id, driver_id) 修改为 idx_station_date_driver(station_id, start_date, driver_id) 。优化后的结果如下图所示。

4.png

拓展

索引失效常见场景

  • 违反最左前缀匹配原则。例如有索引index(a,b),但查询条件只有b字段。

  • 在索引列上做任何操作,包括计算、函数、类型转换等。

  • 范围查询使联合索引停止匹配。

  • 减少select*的使用。避免不必要的回表操作开销,尽量使用覆盖索引。

  • 使用不等于(!=、<>),使用or操作。

  • 字符串不加单引号索引失效。

  • like以通配符开头'%abc'。注意like ‘abc%’ 是可以走索引的。

  • order by 违反最左匹配原则,含非索引字段排序,会产生文件排序。

  • group by 违反最左匹配原则,含非索引字段分组,会导致产生临时表。

Explain分析

慢查询的分析离不开mysql的explain语句,explain主要关注两个字段Type和Extra。

Type表示访问数据的方式,Extra表示过滤和整理数据的方式。这里列举出来方便查找。

Type
Extra
ALL 全表扫描 Using index 使用覆盖索引,不需要回表,不需要Mysql服务层过滤
index 索引树全扫描 Using where 从存储引擎层获取数据,在Mysql服务层用where查询条件过滤数据。
range 索引树范围扫描 Using where; Using index 索引范围扫描。索引扫描和全表扫描类似,只是发生的层面不一样。
ref 非唯一性索引扫描,比如非唯一索引和唯一索引的非唯一前缀 Using index condition 使用索引下推,在存储引擎层充分利用查询索引字段过滤数据
eq_ref 唯一性索引扫描,比如唯一索引、主键索引 Using temporary 临时表存储结果,用于排序和分组查询
const 将查询转化成常量 Using filesort 文件排序,用于排序
NULL 不用访问表或索引 NULL 回表

更多编程相关知识,请访问:编程入门!!

以上是Mysql索引失效怎么办?失效原因浅析的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Laravel 教程
1602
29
PHP教程
1504
276
如何在PHP环境中设置环境变量 PHP运行环境变量添加说明 如何在PHP环境中设置环境变量 PHP运行环境变量添加说明 Jul 25, 2025 pm 08:33 PM

PHP设置环境变量主要有三种方式:1.通过php.ini全局配置;2.通过Web服务器(如Apache的SetEnv或Nginx的fastcgi_param)传递;3.在PHP脚本中使用putenv()函数。其中,php.ini适用于全局且不常变的配置,Web服务器配置适用于需要隔离的场景,putenv()适用于临时性的变量。持久化策略包括配置文件(如php.ini或Web服务器配置)、.env文件配合dotenv库加载、CI/CD流程中动态注入变量。安全管理敏感信息应避免硬编码,推荐使用.en

如何用PHP开发AI智能表单系统 PHP智能表单设计与分析 如何用PHP开发AI智能表单系统 PHP智能表单设计与分析 Jul 25, 2025 pm 05:54 PM

选择合适的PHP框架需根据项目需求综合考虑:Laravel适合快速开发,提供EloquentORM和Blade模板引擎,便于数据库操作和动态表单渲染;Symfony更灵活,适合复杂系统;CodeIgniter轻量,适用于对性能要求较高的简单应用。2.确保AI模型准确性需从高质量数据训练、合理选择评估指标(如准确率、召回率、F1值)、定期性能评估与模型调优入手,并通过单元测试和集成测试保障代码质量,同时持续监控输入数据以防止数据漂移。3.保护用户隐私需采取多项措施:对敏感数据进行加密存储(如AES

如何用PHP搭建在线客服机器人 PHP智能客服实现技术 如何用PHP搭建在线客服机器人 PHP智能客服实现技术 Jul 25, 2025 pm 06:57 PM

PHP在智能客服中扮演连接器和大脑中枢角色,负责串联前端输入、数据库存储与外部AI服务;2.实现时需构建多层架构:前端接收用户消息,PHP后端预处理并路由请求,先匹配本地知识库,未命中则调用外部AI服务如OpenAI或Dialogflow获取智能回复;3.会话管理由PHP写入MySQL等数据库,保障上下文连续性;4.集成AI服务需用Guzzle发送HTTP请求,安全存储APIKey,做好错误处理与响应解析;5.数据库设计需包含会话、消息、知识库、用户表,合理建索引、保障安全与性能,支撑机器人记忆

如何让PHP容器支持自动构建 PHP环境持续集成CI配置方式 如何让PHP容器支持自动构建 PHP环境持续集成CI配置方式 Jul 25, 2025 pm 08:54 PM

要让PHP容器支持自动构建,核心在于配置持续集成(CI)流程。1.使用Dockerfile定义PHP环境,包括基础镜像、扩展安装、依赖管理和权限设置;2.配置GitLabCI等CI/CD工具,通过.gitlab-ci.yml文件定义build、test和deploy阶段,实现自动构建、测试和部署;3.集成PHPUnit等测试框架,确保代码变更后自动运行测试;4.使用Kubernetes等自动化部署策略,通过deployment.yaml文件定义部署配置;5.优化Dockerfile,采用多阶段构

如何用PHP结合AI做视频内容分析 PHP智能视频标签生成 如何用PHP结合AI做视频内容分析 PHP智能视频标签生成 Jul 25, 2025 pm 06:15 PM

PHP结合AI做视频内容分析的核心思路是让PHP作为后端“胶水”,先上传视频到云存储,再调用AI服务(如GoogleCloudVideoAI等)进行异步分析;2.PHP解析返回的JSON结果,提取人物、物体、场景、语音等信息生成智能标签并存入数据库;3.优势在于利用PHP成熟的Web生态快速集成AI能力,适合已有PHP系统的项目高效落地;4.常见挑战包括大文件处理(用预签名URL直传云存储)、异步任务(引入消息队列)、成本控制(按需分析 预算监控)和结果优化(标签规范化);5.智能标签显着提升视

如何搭建独立PHP任务容器环境 PHP定时脚本运行容器配置方法 如何搭建独立PHP任务容器环境 PHP定时脚本运行容器配置方法 Jul 25, 2025 pm 07:27 PM

搭建独立PHP任务容器环境可通过Docker实现,具体步骤如下:1.安装Docker与DockerCompose作为基础;2.创建独立目录存放Dockerfile、crontab文件;3.编写Dockerfile定义PHPCLI环境并安装cron及必要扩展;4.编写crontab文件定义定时任务;5.编写docker-compose.yml挂载脚本目录并配置环境变量;6.启动容器并验证日志。相比Web容器内执行定时任务,独立容器具备资源隔离、环境纯粹、稳定性强、便于扩展等优势。为确保日志与错误捕

如何用PHP构建日志管理系统 PHP日志采集与分析工具 如何用PHP构建日志管理系统 PHP日志采集与分析工具 Jul 25, 2025 pm 08:48 PM

选择日志记录方式:初期可用PHP内置error_log(),项目扩大后务必切换至Monolog等成熟库,支持多handler和日志级别,确保日志含时间戳、级别、文件行号及错误详情;2.设计存储结构:小量日志可文件存储,大量或需分析则选数据库,结构化数据用MySQL/PostgreSQL,半结构化/非结构化推荐Elasticsearch Kibana,同时制定备份与定期清理策略;3.开发分析界面:应具备搜索、过滤、聚合、可视化功能,可直接集成Kibana,或用PHP框架 图表库自研,注重界面简洁易

如何利用Kubernetes保持PHP环境一致 生产和本地容器配置标准 如何利用Kubernetes保持PHP环境一致 生产和本地容器配置标准 Jul 25, 2025 pm 06:21 PM

要解决PHP环境在本地与生产之间不一致的问题,核心在于利用Kubernetes的容器化与编排能力实现环境统一,具体步骤如下:1.构建统一的Docker镜像,包含所有PHP版本、扩展、依赖和Web服务器配置,确保开发与生产使用同一镜像;2.使用Kubernetes的ConfigMap和Secret管理非敏感与敏感配置,通过卷挂载或环境变量注入,实现不同环境配置的灵活切换;3.通过统一的Kubernetes部署定义文件(如Deployment、Service)保障应用行为一致性,并纳入版本控制;4.

See all articles