Mysql 大表limit查询优化原理实战
文章目录
- 1 大表查询无条件优化&原理(入门)
- 2 大表查询带 条件 优化&原理(进阶)
- 2.1 where 后面的查询字段只有一个时,要求该字段是索引字段
- 2.2 where 后面的查询字段有多个时,尽量让查询字段为索引字段且字段值基数大
- 3 大表查询带 排序 优化&原理(进阶)
- 3.1 没有where 条件,那么推荐使用 order by create_time desc。
- 3.2 where 条件查询字段中,如果存在索引字段 ,且当前查询不需要回表,那么推荐使用 order by id desc
- 3.查询字段加了索引,一定会用上吗(最左匹配原则-模糊搜索案例~ 会颠覆你的三观)
1 大表查询无条件优化&原理(入门)
优化前( 查询耗时 114.1s)
select * from link_exec_task limit 80000, 10 # 查询耗时 114.1s
优化后( 查询耗时 0.121s)
select * from link_exec_task a
INNER JOIN (select id from link_exec_task limit 80000, 10) b on a.id = b.id #0.121s
原理(索引覆盖+非聚簇索引):
观察以下sql 查询计划
查询优化原理::
主键ID形成的索引是聚簇索引(B+树),叶子节点存的是记录(数据);而普通索引字段形成的索引是非聚簇索引,非聚簇索引的叶子节点里面存的是记录的ID。
查找第8w条记录,聚簇索引和非聚簇索引 查找对比:
聚簇索引方式:由于聚簇索引-数据页里面的数据项存的是完整的数据记录,故而一个数据页里面的数据项占用空间大,即一个数据页只能存少数的数据项,即一次磁盘IO查询出来的数据页只包含少数的数据项。因此当limit 偏移量很大时,就得通过多次的磁盘IO来查找对应的偏移量的记录,很慢!
非聚簇索引方式:由于非聚簇索引-数据页里面的数据项存的是记录的ID,故而一个数据页里面的数据项占用空间很小,即一个数据页可以存更多的数据项,即一次磁盘IO查询出来的数据页只包含很多个数据项。因此当limit 偏移量很大时,只select id的情况下,只需通过少次数的磁盘IO就能快速找到对应偏移量的记录ID,很快~。
故而上面就算我查询哪怕没有用到 status字段进行查询,Innodb执行引擎也会借助status索引字段使用的非聚簇索引,来快使找到第8w条记录的ID。
问1:为什么借助的是status索引字段,而不使用其他非聚簇索引字段?
答:我的理解是 会挑一个索引字段空间占用最小的字段,在该表的索引字段中,就status字段的空间占用最小。因为这里借助非聚簇索引的目的是找出数据页里面的id值,而不关心目录页里面的索引字段值,由于目录页存的内容是索引字段值和数据页的地址,故而索引字段越小,那么一个目录页能存放更多的目录项,这些目录页又可以指向数据页的地址,故而可以通过更少次数的IO来找到更多的数据页。
问2:子查询里面可能select id吗?我还能select 其他吗?
答:正常情况下不行,例如你select * ,那么此时你的查询耗时会和优化前查询耗时一样,因为此时你的查询不是覆盖索引查询,由于非聚簇索引里面没有你要select的内容,故而innodb直接就不借助非聚簇索引找第8w条记录的ID;而是直接从聚簇索引里面找出第8w条记录,故而速度会变得很慢。
当然,如果你select 的字段均包含在某个非聚簇索引树里面,那么此时还是会使用覆盖索引,还是走非聚簇索引,查询速度也会快。
问3:上文的业务逻辑是查询第8W条数据之后的10条记录,假设使用聚簇索引进行扫描,那么只需扫描数据页下的8w条数据项;那么innodb借助了status非聚簇索引进行查询第8w条记录之后的10个ID,是否也只需要扫描8w条数据项?
答:一般不止。因为在聚簇索引中,数据页里面的数据项(完整数据记录)的ID是升序的。但是在非聚簇索引中,索引结构是根据索引字段值进行排序,所以数据页里面的数据项的ID是无序的。所以我的理解是,innodb需要把非聚簇索引所有数据页里面所有的数据项的ID都给查询出来,然后再根据ID进行排序,最终返回ID第8w大的10条ID记录。
2 大表查询带 条件 优化&原理(进阶)
经过上文的学习,我们找到大表主要可以利用 非聚簇索引+覆盖索引 的方式进行目标记录ID的快速查找。
select * from link_exec_task a
INNER JOIN (select id from link_exec_task limit 80000, 10) b on a.id = b.id
这一节主要讲 “select id from link_exec_task limit 80000, 10” 这部分 加条件和排序 的优化。因此下面的示例也是针对该部分的sql进行改造。
2.1 where 后面的查询字段只有一个时,要求该字段是索引字段
以下文SQL为例:
假设status字段不是索引字段:那么innodb就无法找到 包含status字段的非聚簇索引树,故而只能利用聚簇索引树进行数据过滤,而聚簇索引的一次磁盘IO只能扫描到少数量的数据项(上一节的知识),故而该SQL查询非常耗时!
假设status字段是索引字段:那么innodb就可以找到 status字段对应的非聚簇索引树,根据该非聚簇索引快速找到符合查询条件的数据页,由于非聚簇索引的数据页里面的数据项只有记录ID(上一节的知识),即一个数据页可以包含大量的数据项。故而只需要少量的数据页就能表示符合条件的数据项,故而通过少次数的磁盘IO就能把对应的数据页给查找出来,故而该SQL查询非常快!
select id from link_exec_task where status=3 limit 80000, 10
2.2 where 后面的查询字段有多个时,尽量让查询字段为索引字段且字段值基数大
注:字段值基数大是指 字段值重复率低,越低越好。例如ID的基数大,因为ID不能重复。基数越大,越有利于查找树的快速查找。
(1)以下文SQL为例,假设status字段是索引字段,而scene_type字段不是索引字段。status字段值基数大、小 时的查询耗时:
经过status过滤后,此时会根据符合status条件的这些ID进行回表,然后根据ID对应的所有记录,然后再根据 scene_type = 1 进行过滤,筛选和合适的ID。如果status字段基数很大,那么回表次数会很少,此时总体查询耗时短;反之,回表次数会很多,从而造成查询耗时急剧加长。
select id from link_exec_task where status=3 and edge_id = '04001' limit 80000, 10
(2)以下文SQL为例,假设status字段是索引字段,而edge_id 字段也是索引字段。
此时innodb会进行优化分析,有可能将这两个索引进行合并,即索引合并index_merge,也有可能只使用其中一个索引。
如果使用了索引合并index_merge:那么查询速度会很快,因为此时innodb会使用status和edge_id 这两个非聚簇索引树,分别根据查询条件进行过滤,过滤出两组ID集合后,再对ID集合取交集(因为条件是and方式),此过程无需回表,就可以找出符合条件的ID集合。
如果只使用其中一个索引:那么查询耗时就取决于作为索引的那个字段值基数大不大,如果基数大,那么耗时短,否则耗时就很长,具体原因见上文(1)。
select id from link_exec_task where status=3 and edge_id = '04001' limit 80000, 10
问:什么时候会使用索引合并,什么时候只会使用其中一个索引。
答:和你字段范围、字段的存储内容、条件查询内容有关。理论上,表数据量大 + limit offert偏移量大 情况下,索引合并 会比 “只使用其中一个索引” 的查询速度快很多。但有时候innodb就是会选择“只使用一个索引”,导致优化后查询速度反而变慢,这玩意儿感觉也有点捉摸不透,让人蛋疼。
相同查询条件,查询内容不一样,实际使用的索引可能也不一样。
条件里面有多个索引,只实际使用一个聚簇索引可能导致的查询急剧下降:
见下图:
3 大表查询带 排序 优化&原理(进阶)
假设当前表中,有id主键字段,也有creat_time索引字段,前端需要展示最新记录,那么此时就必须使用id或者create_time 进行倒序排序,具体使用哪个字段呢?选择合适排序字段能让你的查询速度天差地别。
3.1 没有where 条件,那么推荐使用 order by create_time desc。
结合下图的explain结果:
见下图无where条件时,使用id和create_time进行倒序排序,时间相差甚大,这是为什么?
如果没有where条件且使用id进行倒序排序时:由于id没有专门的非聚簇索引树,所以innodb使用的是聚簇索引查找,从聚簇索引里面的最后一个数据页的数据项从后晚前找,直到找到第8w条数据项之后的10条记录id。上文已经说过,聚簇索引的数据页里面的数据项是完整的数据记录,即一个数据项占用空间很大,一个数据页只能包含少量的数据项,故而需要查找大量的数据页,即通过大量的磁盘IO 才能把这8w条数据项所在数据页给扫描出来,速度很慢。
如果没有where条件且使用ceate_time进行倒序排序时:由于create_time有专门的非聚簇索引树,此时innodb使用的是非聚簇索引查找,从ceate_time非聚簇索引里面的最后一个数据页的数据项从后晚前找,直到找到第8w条数据项之后的10条记录id。上文已经说过,非聚簇索引的数据页里面的数据项是记录ID,即一个数据项占用空间很小,一个数据页可以包含大量的数据项,故而只需查找少量的数据页,即通过少量的磁盘IO 就能把这8w条数据项所在数据页给扫描出来,速度很慢。
以下SQL查询耗时:27.166s
select id from link_exec_task ORDER BY id desc limit 100000, 10 # 查询耗时:27.166s
以下SQL查询耗时:0.143s
select id from link_exec_task ORDER BY id desc limit 100000, 10 # 查询耗时:0.143s
3.2 where 条件查询字段中,如果存在索引字段 ,且当前查询不需要回表,那么推荐使用 order by id desc
结合下图的explain结果:
看explain分析结果,再结合上文那些说明,其实你也应该懂了。
答:where 条件查询字段中,存在status索引查询字段 ,根据条件status=0 innodb使用status非聚簇索引树过滤掉数据后,拿到符合条件的记录ID集合,此时分以下两种情况:
假设当前是根据id进行排序排序: 将过滤后ID集合进行快速倒序排序,然后找出第10w条记录后面的10条记录ID,不需要回表,故而速度很快。
假设当前是根据create_time进行排序排序: 由于当前是create_time字段进行倒序排序,而且上一步status=0过滤后只有记录的ID集合,innodb执行引擎没办法对这些ID集合进行create_time倒序排序,故而只能拿着过滤后的ID集合,到聚簇索引树进行查找记录,即回表,根据ID对应的create_time值后,再根据ID所对应的Create_time值,对ID集合进行排序。由于我这张表中,status字段基数值不大,也就是status=0过滤后的ID集合还是很大,就会导致后面出现频繁回表,从而造成查询速度急剧下降。
以下SQL执行耗时:29.082s
select id from link_exec_task where status = 0 ORDER BY create_time desc limit 100000, 10
以下SQL执行耗时:0.057s
select id from link_exec_task where status = 0 ORDER BY id desc limit 100000, 10
3.查询字段加了索引,一定会用上吗(最左匹配原则-模糊搜索案例~ 会颠覆你的三观)
答案是不一定,innodb会根据 你当前 是否有使用limit分页、分页的偏移量大小、查询字段存储值基数大小、条件查询内容、排序字段 等 来决定当前使不使用 该查询字段索引。‘’
下面以 分页的偏移量大小、以及是否有使用limit分页 影响innodb查询方式进行演示:
下这里我的name字段配置了普通索引,理论来说,使用最左匹配原则的模糊搜索就能使用到索引,但事实真的是这样吗?见下图。
下面两个图中,我对name使用最左匹配原则的模糊搜索,但实际上却没有使用到name索引,也就是没有走非聚簇索引。而是走了主键索引,从而导致我的查询速度极速下降。 这innodb的优化还不如不优化!!!!
把limit偏移量调小:
查询耗时:22.153s
select id from link_exec_task where name like '定时%' order by id desc limit 0, 10 # 查询耗时 22.153s
把limit偏移量调大:
我把上面的sql limit偏移量调到5000,结果查询速度反而快了几百倍,见下图:
查询耗时:0.095s
select id from link_exec_task where name like '定时%' order by id desc limit 5000, 10 # 查询耗时 0.095s
上面说了,使用limit 偏移量小慢,偏移量大反而块。如果我直接把分页limit 给去掉,是怎么样子的呢?
直接把limit偏移量去掉:
查询耗时:0.075s
select id from link_exec_task where name like '定时%' order by id desc # 查询耗时 0.075s
总结:至此,我们明白了有时候我们使用 索引字段进行搜索,但实际不一定会使用到该索引,影响使用哪种索引的因素很多,具体使用哪种索引还是由innodb决定。要注意的是,innodb优化 有时候不一定会加快查询,反而导致查询速度变慢几十、甚至几百倍。例如像上文的 最左匹配原则的模糊搜索 理论上走普通字段的非聚簇索引更快,但是它有时候却使用了主键索引;有例如有时候 查询使用到了多索引字段查询,但实际没有使用索引合并,而是只使用了其中一个索引,造成查询速度变慢。
相关文章:

Mysql 大表limit查询优化原理实战
文章目录 1 大表查询无条件优化&原理(入门)2 大表查询带 条件 优化&原理(进阶)2.1 where 后面的查询字段只有一个时,要求该字段是索引字段2.2 where 后面的查询字段有多个时,尽量让查询字段为索引字段且字段值基数大 3 大表查询带 排序 优化&…...

在vscode中开发运行uni-app项目
确保电脑已经安装配置好了node、vue等相关环境依赖 进行项目的创建 vue create -p dcloudio/uni-preset-vue 项目名 vue create -p dcloudio/uni-preset-vue uni-app 选择模版 这里选择【默认模版】 项目创建成功后在vscode中打开 第一次打开项目 pages.json 文件会报错&a…...

【JavaEE初阶 — 多线程】Thread的常见构造方法&属性
目录 Thread类的属性 1.Thread 的常见构造方法 2.Thread 的几个常见属性 2.1 前台线程与后台线程 2.2 setDaemon() 2.3 isAlive() Thread类的属性 Thread 类是JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread 对象与之关联&am…...

ctfshow(316)--XSS漏洞--反射性XSS
Web316 进入界面: 审计 显示是关于反射性XSS的题目。 思路 首先想到利用XSS平台解题,看其他师傅的wp提示flag是在cookie中。 当前页面的cookie是flagyou%20are%20not%20admin%20no%20flag。 但是这里我使用XSS平台,显示的cookie还是这样…...
ubuntu22.04安装conda
在 Ubuntu 22.04 上安装 Conda 可以通过以下步骤进行: 下载 Miniconda(轻量级版本的 Conda): 打开终端并运行以下命令以下载 Miniconda 安装脚本: wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-…...

D58【python 接口自动化学习】- python基础之异常
day58 异常捕获 学习日期:20241104 学习目标:异常 -- 74 自定义异常捕获:如何定义业务异常? 学习笔记: 自定义异常的用途 自定义异常的方法 # 抛出一个异常 # raise ValueError(value is error) # ValueError: val…...

Java项目实战II基于Spring Boot的便利店信息管理系统(开发文档+数据库+源码)
目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在快节奏的…...
Java-日期计算工具类DateCalculator
DateCalculator是用于日期计算的工具类。这个工具类将包括日期的加减、比较、周期计算、日期 范围生成等功能。 import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Period; import java.time.temporal.ChronoUnit;…...

单片机串口接收状态机STM32
单片机串口接收状态机stm32 前言 项目的芯片stm32转国产,国产芯片的串口DMA接收功能测试不通过,所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据 两种方式各有优劣,不过我的芯片已经主频跑…...
ipv6的 fc00(FC00::/7) 和 fec0(FEC0::/10)
ipv6的 fc00(FC00::/7)(唯一本地地址) 替代了 fec0(FEC0::/10) (站点本地地址,已弃用) ipv6的 fc00(FC00::/7) 替代了 fec0(FEC0::/10) , 在IPv6地址中,FC00::/7(通常简写为FC00)和…...
Chat GPT英文学术写作指令
目录 Chat GPT英文学术写作指令 Chat GPT英文学术写作指令 1."为我捉供一些建议和技巧,以提高我的学术写作质最和风格" (Provide me with some suggestions andtips to improve the quality andstyleofmyacademic writing.) 2."帮我提写一个清晰而有逻辑的…...

超详细Pycharm安装汉化教程,Python环境配置和使用指南,Python零基础入门看这个就够了!
包含编程资料、学习路线图、源代码、软件安装包等!【[点击这里]】! PyCharm 是由 JetBrains 打造的一款 Python IDE (集成开发环境,Integrated Development Environment),带有一整套可以帮助用户在使用 Py…...
react-native:解决使用webView后部分场景在安卓10崩溃闪退问题
app闪退问题原因: 安卓7以上版本(7和7以下版本不会出现闪退):在屏幕不可视区域加载webView或者webView不在可视区域内切换页面时app崩溃闪退(在屏幕可视区域加载webView或者webView在可视区域内切换页面不会闪退&…...

大数据工具 flume 的安装配置与使用 (详细版)
参考网址:Flume 1.9用户手册中文版 — 可能是目前翻译最完整的版本了 1,上传安装包 安装包链接:文件下载-奶牛快传 Download |CowTransfer 口令:x8bhcg 1,切换盘符到安装目录 cd /opt/moudles 解压文件…...

智慧城市智慧城市项目方案-大数据平台建设技术方案(原件Word)
第1章 总体说明 1.1 建设背景 1.2 建设目标 1.3 项目建设主要内容 1.4 设计原则 第2章 对项目的理解 2.1 现状分析 2.2 业务需求分析 2.3 功能需求分析 第3章 大数据平台建设方案 3.1 大数据平台总体设计 3.2 大数据平台功能设计 3.3 平台应用 第4章 政策标准保障…...
C语言比较两个字符串是否相同
在不使用string.h中的内置函数的情况下 #include <stdio.h> #include <string.h> void main(){char arr1[]"hello world";char arr2[]"hello world";int i,a0;if(strlen(arr1)!strlen(arr2)){print("不相等");return 0;}for(i0;arr…...

丹摩征文活动|FLUX.1图像生成模型:AI工程师的创新实践
文章目录 一、对"FLUX.1"系列模型版本分析 二、AI工程师与FLUX.1系列模型 三、ComfyUI在线部署四、FLUX.1在线部署五、添加工作流呈现效果图展示六、总结 黑森林实验室(Black Forest Labs)推出的FLUX.1图像生成模型,凭借120亿参数的…...

ZABBIX API获取监控服务器OS层信息
Zabbix 是一款强大的开源监控解决方案,能够通过其 API 接口自动化管理和获取监控数据。在这篇文章中,详细讲解如何通过 Zabbix API 批量获取服务器的系统名称、IP 地址及操作系统版本信息,并将数据保存到 CSV 文件中。本文适合对 Python 编程和 Zabbix 监控系统有一定基础的…...

SpringBoot基础系列学习(五):JdbcTemplate 访问数据库
文章目录 一丶介绍二丶引入依赖三丶配置配置文件四丶创建表五丶java代码 一丶介绍 Spring Boot作为Spring的集大成者,自然会将JdbcTemplate集成进去。Spring Boot针对JDBC的使用提供了对应的Starter包:spring-boot-starter-jdbc,它其实就是在…...

JavaEE-多线程初阶(3)
目录 1.线程的状态 1.1 NEW、RUNNABLE、TERMINATED 1.2 TIMED_WAITING 1.3 WAITING 1.4 BLOCKED 2.多线程带来的风险-线程安全(重点) 2.1 观察线程不安全的现象 2.2 分析产生该现象的原因 2.3 产生线程安全问题的原因 2.3.1 抢占式执行&#x…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...

spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...