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

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

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

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

背景

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

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

1.png

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

联合索引 字段1 字段2 字段3

idx_date_station_driverstart_datestation_iddriver_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<'1631120399' and status='2' and station_id='1809'),只匹配了索引idx_date_station_driver(start_date, station_id, driver_id)的第一个字段,即只匹配了idx_date_station_driver(start_date),station_id=’1809‘精确查询并没有作用到匹配索引上,而是在Index Filter即索引下推过程中发挥了作用。实际上这里是因为范围查询使联合索引停止匹配

办公小浣熊 办公小浣熊

办公小浣熊是基于商汤大语言模型的原生数据分析产品,

办公小浣熊 77 查看详情 办公小浣熊

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

为什么范围查询会使联合索引停止匹配?这里涉及到最左前缀匹配原理。假设建立一个联合索引 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索引失效怎么办?失效原因浅析的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/289093.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月4日 19:49:14
下一篇 2025年11月4日 19:51:36

相关推荐

  • Python程序:将字符串的第K个索引单词连接起来

    字符串是不可变的数据结构,以字符串格式存储数据。它可以通过使用str()方法或通过在单引号或双引号中给出数据来创建。它访问我们使用索引的字符串的元素。在索引中,我们有负索引和正索引,与负索引一样,我们将使用 -1 和 (-string 的长度) 访问最后一个元素到第一个元素。在正索引中,我们将为第一…

    2025年12月13日
    000
  • PDO多条记录插入:正确处理数组参数的教程

    本教程详细讲解了在使用PHP PDO将数组数据批量插入MySQL数据库时常见的错误及正确方法。重点阐述了如何避免`bindParam`将数组转换为字符串导致的问题,并提供了在循环中通过`execute`方法传递参数的最佳实践,确保数据正确、高效地入库。 在使用PHP的PDO扩展与MySQL数据库交互…

    2025年12月13日
    000
  • php怎么调用数组中的数据库_php数组调用数据库数据循环查询法【技巧】

    PHP中从数据库获取数据并转为数组有五种方法:一、mysqli_fetch_array()逐行提取;二、mysqli_fetch_all()一次性获取二维数组;三、PDO fetch()逐行获取;四、PDO fetchAll()一次性加载全部数据;五、手动构建自定义键名一维数组。 如果您在PHP中需…

    2025年12月13日
    000
  • php源码包怎么升级_php源码包升级步骤与兼容性处理【技巧】

    先备份当前PHP环境,再下载新版源码并解压,检查依赖后用原编译参数配置并编译安装,替换旧文件,重编第三方扩展,最后验证新版本功能与服务运行。 如果您正在运行基于PHP源码编译的环境,并希望将当前版本升级到更新的稳定版本,可能面临模块兼容性、配置迁移和扩展支持等问题。以下是完成PHP源码包升级的关键步…

    2025年12月13日
    000
  • PHP/MySQL多对多关系处理与安全动态表单数据插入指南

    本教程详细阐述了如何在php和mysql中高效且安全地管理多对多数据库关系。我们将通过学生选课系统为例,讲解如何设计中间表、从数据库动态生成html多选框,以及使用php处理表单提交。特别强调了利用mysqli预处理语句来防止sql注入攻击,确保数据交互的安全性与可靠性。 在现代Web应用开发中,处…

    2025年12月13日 好文分享
    000
  • php表白墙源码怎么做网页_用php表白墙源码做网页教程【指南】

    答案:搭建在线表白平台需部署PHP表白墙源码,具体步骤为:一、从可信渠道获取完整源码并检查核心文件与安全性;二、安装XAMPP等集成环境,启动Apache和MySQL服务,将源码放入htdocs或www目录;三、通过phpMyAdmin创建数据库biaobai_wall并导入源码附带的SQL文件;四…

    2025年12月13日
    000
  • PHP表单数据动态收集与持久化:使用Session管理

    本教程详细讲解如何在php中实现表单数据的动态收集与持久化存储到数组。针对每次表单提交数据丢失的问题,我们将介绍如何利用php session机制来维护数组状态,确保用户提交的数据能够累积保存,并提供完整的代码示例和实现步骤,帮助开发者构建动态数据收集应用。 理解挑战:HTTP的无状态性 在Web开…

    2025年12月13日
    000
  • php读取sql二维数组操作_php数据库结果转数组方法【指南】

    应使用mysqli_fetch_all()、PDO::fetchAll()、手动循环fetch_assoc()或array_map配合fetch_row等方法将数据库结果转为二维数组,以适配不同扩展和需求场景。 如果您从PHP数据库查询中获取了结果集,但需要将其转换为二维数组以便于后续处理,则可能是…

    2025年12月13日
    000
  • .php源码怎么运行_php源码运行环境搭建与执行

    要运行PHP源码需先搭建运行环境。1、安装XAMPP并启动Apache,将.php文件放入htdocs目录,通过http://localhost访问;2、或使用PHP内置服务器,在文件所在目录执行php -S localhost:8000,再在浏览器中访问对应地址;3、也可配置Nginx与PHP-F…

    2025年12月13日
    000
  • 使用PHP从MySQL获取多张表数据并结构化为JSON的教程

    本教程详细介绍了如何使用php连接mysql数据库,执行多条sql查询以获取不同表的数据,然后将这些数据分别收集到php数组中,并最终整合成一个统一的关联数组,通过`json_encode`函数将其转换为结构化的json格式输出。这为前端应用提供了一种高效的数据接口实现方式。 引言 在现代Web应用…

    2025年12月13日
    000
  • PHP中从多个MySQL表获取数据并输出为结构化JSON的教程

    本教程详细介绍了如何使用php从mysql数据库中的多个独立表(如`countries`、`cities`、`neighborhoods`)检索数据,并将这些数据组织成一个统一的json对象。文章通过分步指南和示例代码,演示了如何建立数据库连接、执行多条查询、处理结果集,并最终将不同表的数据以各自的…

    2025年12月13日
    000
  • phpMyAdmin数据库导出导入时区错误解决方案

    在phpMyAdmin环境中进行数据库导出和导入是常见的操作,但有时用户会遇到“未知或不正确的时区”(#1298)错误,尤其是在尝试导入由phpMyAdmin导出的SQL文件时。此错误通常表现为SQL语句 `SET time_zone = “+00:00″` 导致导入失败。本…

    2025年12月13日
    000
  • php怎么把结果集转换为数组_PHP将结果集转换为数组格式

    需根据数据库扩展选择转换方式:mysqli用fetch_all()等方法,PDO用fetchAll()指定模式,废弃的mysql扩展须迁移;多表JOIN需手动嵌套构造数组;NULL值及类型需按需统一处理。 如果您从数据库查询获取了结果集,但需要将其转换为标准的PHP数组以便进一步处理,则需根据所使用…

    2025年12月13日
    000
  • 在Docker多阶段构建中为Laravel应用定制Composer的PHP版本

    本文探讨了在docker多阶段构建中,如何解决composer镜像默认php版本与laravel应用依赖不兼容的问题。文章提供了三种解决方案:使用特定版本的composer镜像、基于alpine手动安装php,以及推荐的基于明确php版本镜像安装composer。旨在帮助开发者优化镜像大小、确保应用…

    2025年12月13日
    000
  • PHP与MySQL:解决多输入字段批量更新的表单数据处理策略

    本文详细阐述了在PHP中处理包含多个动态生成输入字段的表单,并实现MySQL数据库批量更新的最佳实践。核心在于利用HTML输入字段的数组命名机制,结合后端PHP的循环处理,确保所有数据都能被正确捕获和更新。同时,教程强调了使用数据库主键作为唯一标识符的重要性,并提供了两种优化方案,以提升数据处理的效…

    2025年12月13日
    000
  • php数组怎么添加数据库中_PHP数组数据添加到数据库操作

    需将PHP数组转为SQL语句再插入数据库,常用方式有五种:一、PDO预处理批量插入;二、mysqli逐条插入;三、JSON编码存TEXT字段;四、ORM框架如Eloquent批量写入;五、LOAD DATA INFILE导入CSV。 如果需要将PHP数组中的数据插入数据库,必须先将数组结构转换为可执…

    2025年12月13日
    000
  • PHP单文件实现动态发帖与即时显示教程

    本教程旨在解决php发帖系统中常见的帖子提交后需刷新页面才能显示,并可能导致数据重复录入的问题。我们将深入探讨http请求机制,并介绍“单php脚本/自提交”模式,通过将表单处理和内容显示逻辑整合到同一文件中,实现用户发帖后内容即时显示,同时确保数据仅单次准确写入数据库,从而优化用户体验和系统效率。…

    2025年12月13日
    000
  • 如何有效防止PHP表单在页面加载或刷新时自动提交

    本文深入探讨了在PHP Web应用中防止表单在页面加载或刷新时自动提交的核心策略。通过详细介绍Post/Redirect/Get (PRG) 设计模式,文章阐述了其工作原理及在PHP中的具体实现,包括使用header(“Location: …”)进行重定向。此外,…

    2025年12月13日
    000
  • 解决PHP MySQLi预处理语句在循环中变量值残留问题

    本文旨在解决php中使用mysqli预处理语句在循环中查询数据时,由于`bind_result`的变量引用特性导致前次查询结果残留的问题。当某些记录不存在时,变量会保留上一个成功查询的值而非重置为`null`。教程将详细解释这一现象,并提供两种有效的解决方案:在循环内部显式重置变量为`null`或使…

    2025年12月13日
    000
  • php数组怎么存进数据库中_PHP将数组存储到数据库的方法

    PHP数组存库需先转字符串:一、serialize()序列化,兼容强且保结构;二、json_encode()转JSON,跨语言可读;三、拆分存关联表,适合独立实体;四、implode()拼接字符串,仅限一维简单数组;五、base64编码序列化结果,用于避特殊字符。 如果您需要将PHP数组保存到数据库…

    2025年12月13日
    000

发表回复

登录后才能评论
关注微信