MySQL的优化利器:索引条件下推,千万数据下性能提升273%

news2024/11/14 7:04:52

MySQL的优化利器:索引条件下推,千万数据下性能提升273%🚀

前言

上个阶段,我们聊过MySQL中字段类型的选择,感叹不同类型在千万数据下的性能差异

时间类型:MySQL字段的时间类型该如何选择?千万数据下性能提升10%~30%🚀

字符类型:MySQL字段的字符类型该如何选择?千万数据下varchar和char性能竟然相差30%🚀

新的阶段我们来聊聊MySQL中索引的优化措施,本篇文章主要聊聊MySQL中的索引条件下推

同学们可以带着这些问题来看本篇文章:

  1. MySQL中多查询条件的语句是如何执行的?server层与存储引擎层如何交互?
  2. 聚簇索引和二级索引存储内容的区别?
  3. 什么是回表?回表有哪些开销?如何避免回表?
  4. 什么是索引条件下推?
  5. 什么时候可以用上索引条件下推?
  6. 索引条件下推能解决什么问题?
  7. 千万数据量下索引条件下推能提升多少性能?

server层与存储引擎层

MySQL服务端可以分为server层与存储引擎层,存储引擎层主要存储记录,可以用不同的存储引擎实现(innodb,myisam)

server层有不同的组件处理不同的功能,比如:接收客户端请求(连接器)、检查SQL语法(分析器)、判断缓存命中(查询缓存8.0移除)、优化SQL和选择索引生成执行计划(优化器)、调用存储引擎获取记录(执行器)

image.png

server层与存储引擎层的交互

以学生表为例

CREATE TABLE `student` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `age` smallint(6) DEFAULT NULL COMMENT '年龄',
  `student_name` varchar(20) DEFAULT NULL COMMENT '名称',
  `info` varchar(30) DEFAULT NULL COMMENT '信息',
  PRIMARY KEY (`id`),
  KEY `idx_age_name` (`age`,`student_name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

聚簇(主键)索引以主键id有序存储整个记录的值

image.png

二级索引只存储规定的索引列和主键,并且以索引列、主键值的先后顺序有序

二级索引为(age,student_name)联合索引时整体上age有序,当age相等时,student_name有序,当student_name相等时,主键有序

image.png

当发生多条件查询时(where 有多个条件),执行器从存储引擎层获取完数据还需要在server层过滤其他查询条件

比如select * from student where age = 18 and student_name like 'c%'; (查询学生表中年龄为18,名称为c开头的学生)

存在(age,student_name)的联合索引,优化器会认为联合索引是最优的,于是生成使用(age,student_name)联合索引的执行计划,执行器根据执行计划调用存储引擎层

在存储引擎层会根据age = 18进行匹配,当满足此条件时,先回表查询聚簇索引

什么是回表?

二级索引只存储需要的列和主键,聚簇(主键)索引存储所有数据

由于我们使用的索引没有存储查询列表需要的列,于是需要去聚簇(主键)索引中再次查询获取其他列的值

image.png

在这个过程中主键值可能是乱序的,因此回表查询聚簇索引时,会出现随机IO(开销大)

server层与存储引擎层交互的单位是记录

image.png

  1. server层优化器根据索引生成执行计划,执行器调用存储引擎层
  2. 存储引擎层在联合索引中寻找满足 age=18的记录
  3. 每次找到记录回表查询聚簇索引获取其他列的值
  4. 然后返回给server层进行where过滤
  5. 2-4实际是一个循环,直到找到第一条不满足条件的记录

在这个流程中会发现一个问题:student_name like 'c%'可以在存储引擎层的联合索引中就判断,并不需要回表查询聚簇索引后返回server层判断

索引条件下推 Index Condition Push

索引条件下推英文名:Index Condition Push

将判断where条件从server层下推到存储引擎层,也就是说存储引擎层也会判断查询其他条件

比如age=18 and student_name like 'c%',在回表前还需要判断student_name是否满足

图中第一条和第三条记录不满足student_name like 'c%'因此不回表直接跳过

image.png

索引条件下推ICP 防止明明可以在存储引擎层判断,但还回表查询后拿到server层判断,减少回表次数

image.png

加入ICP后的执行步骤:

  1. server层优化器根据索引生成执行计划,执行器调用存储引擎层
  2. 存储引擎层在索引上查找满足age=18的记录
  3. 找到满足条件的记录后,根据索引上现有列判断其他查询条件,不满足则跳过该记录
  4. 满足则回表查询聚簇索引其他列的值
  5. 获取需要查询的值后,返回server层进行where过滤
  6. 2-5步骤为循环执行,直到找到第一条不满足条件的记录

测试

开启函数创建

#开启函数创建
set global log_bin_trust_function_creators=1;

#ON表示已开启
show variables like 'log_bin_trust%';

定义随机生成字符串函数

#分割符从;改为$$
delimiter $$
#函数名ran_string 需要一个参数int类型 返回类型varchar(255)
create function ran_string(n int) returns varchar(255)
begin
#声明变量chars_str默认'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
#声明变量return_str默认''
declare return_str varchar(255) default '';
#声明变量i默认0
declare i int default 0;
#循环条件 i<n
while i < n do
set return_str = concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i=i+1;
end while;
return return_str;
end $$

定义范围生成整形函数

#生成范围生成整形的函数
delimiter $$
create function range_nums(min_num int(10),max_num int(10)) returns int(5)
begin
declare i int default 0;
set i = FLOOR(RAND() * (max_num - min_num + 1)) + min_num;
return i;
end $$

定义插入函数

#插入 从参数start开始 插入max_num条数据
delimiter $$ 
create procedure insert_students_tests(in start int(10),in max_num int(10))
begin
declare i int default start;
set autocommit = 0;
repeat
set i = i+1;
#SQL 语句
insert into student(student_name,age,info) 
values (ran_string(10),range_nums(0,100),ran_string(20));
until i=max_num
end repeat;
commit;
end $$

执行

#执行插入函数
delimiter ;
call insert_students_tests(0,19000000);

我测试的数据量是1900百万

记得建立索引

alter table student add index idx_age_name(age,student_name);

索引条件下推默认情况是开启的,SQL_NO_CACHE是不使用缓存(MySQL5.7 版本还有缓存)

select SQL_NO_CACHE * from student where age = 18 and student_name like 'c%'
> OK
> 时间: 1.339s

那如何判断是否使用到索引条件下推呢?

我们使用explain查看执行计划,当附加信息中存在Using index condition说明使用索引条件下推

image.png

那如何关闭索引条件下推呢?

这里我们使用会话级别的关闭

SET optimizer_switch = 'index_condition_pushdown=off';

关闭后,再查看执行计划发现附加信息中不再有Using index condition

image.png

select SQL_NO_CACHE * from student where age = 18 and student_name like 'c%'
> OK
> 时间: 5.039s

(5.039 - 1.339) / 1.339 = 276% ,使用索引条件下推提升的性能竟为 276%

经过前面的分析,索引条件下推是通过减少回表的次数从而优化性能,因此这里提升的性能实际上节省不必要的回表开销

在查询大数据量情况下,回表不仅要多查聚簇索引,还可能导致随机IO(增加与磁盘的交互)

虽然可以通过索引条件下推优化减少回表次数,但还是会有符合条件的记录需要回表

那有没有什么办法可以尽量避免回表或让回表的开销变小呢?

如果在二级索引上就已经得到需要查询的列(比如查询age,student_name,id),那么就不用回表

那如果还是要去聚簇索引查询其他列,该如何降低回表的开销呢?

这个问题留着下一章讨论,如果你想到什么方案也可以在评论区交流喔~

总结

MySQL服务端分为server层与存储引擎层,存储引擎层可以通过不同的实现(innodb,myisam)存储记录

server层拥有分工明确的不同组件:连接器(管理请求连接)、分析器(处理SQL语法、词性分析)、优化器(优化SQL,根据不同索引生成执行计划)、执行器(根据执行计划调用存储引擎获取记录)

server层与存储引擎层以记录为单位进行交互,server层执行器根据执行计划调用存储引擎层获取记录

二级索引存储索引列和主键的值,并以索引列、主键进行排序,有多个索引列时,前一个索引列相等时当前索引列才有序;聚簇索引存储整条记录的值,并以主键有序

当使用二级索引并且二级索引上的列不满足查询条件时,需要回表查询聚簇索引获取其他列的值;回表查询聚簇索引时主键值无序可能导致随机IO

索引条件下推在多查询条件的情况下,在存储引擎层多判断一次where其他查询条件,利用二级索引上的其他列判断记录是否满足其他查询条件,如果不满足则不用回表,减少回表次数

查询数据量大的情况下,回表的开销非常大,只有当二级索引存在的列满足查询需要的列时才不会回表,回表产生的随机IO要通过其他手段优化

最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 由点到线,由线到面,构建MySQL知识体系,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 gitee-StudyJava、 github-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1133973.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA毕业设计104—基于Java+Springboot+Vue的医院预约挂号小程序(源码+数据库)

基于JavaSpringbootVue的医院预约挂号小程序(源码数据库)104 一、系统介绍 本系统前后端分离带小程序 小程序&#xff08;用户端&#xff09;&#xff0c;后台管理系统&#xff08;管理员&#xff0c;医生&#xff09; 小程序&#xff1a; 预约挂号&#xff0c;就诊充值&…

GWAS软件包:GAPIT3它来啦

GAPIT是一款非常老的而且非常流行的软件包&#xff0c;傻瓜式操作&#xff0c;一键出图出结果&#xff0c;一篮子的解决方案&#xff0c;是我最经常使用的GWAS分析软件包。 最近&#xff0c;GAPIT现在的版本是GAPIT3&#xff0c;速度比第二版有较大的提升&#xff1a; 更大的变…

快速解决安装ps打开找不到MSVCP140.dll问题,教你5个解决方法,

如果你在安装 Photoshop 时遇到找不到MSVCP140.dll的问题&#xff0c;MSVCP140.dll是Microsoft Visual C 2015 Redistributable的一个组件&#xff0c;它提供了许多常用的C函数库&#xff0c;用于支持各种软件的正常运行。当安装或运行某些软件时&#xff0c;如果系统中MSVCP14…

鱼眼图像去畸变python / c++

#鱼眼模型参考链接 本文假设去畸变后的图像与原图大小一样大。由于去畸变后的图像符合针孔投影模型&#xff0c;因此不同的去畸变焦距得到不同的视场大小&#xff0c;且物体的分辨率也不同。可以见上图&#xff0c;当焦距缩小为一半时&#xff0c;相同大小的图像&#xff08;横…

【办公常用软件分享】

在平时的工作生活中&#xff0c;经常会遇到各种各样的需求&#xff0c;没有合适的工具&#xff0c;不仅会降低效率&#xff0c;还会影响结果&#xff0c;有些工具的功能虽然能够满足&#xff0c;但是需要付费&#xff0c;偶尔用一次总显得不划算&#xff0c;所以今天就分享几个…

Modbus转Profinet网关连接三菱变频器博图快速配置案例

本案例将分享如何使用兴达易控的modbus转profinet网关&#xff08;XD-MDPN100&#xff09;来连接西门子1200系列plc&#xff0c;并实现三菱变频器的485通讯兼容转modbusTCP通信。通过在博图中进行配置&#xff0c;我们可以实现设备之间的连接和通信。 首先&#xff0c;我们需…

通信基础(三):多路复用技术

一、时分复用 时分复用造成线路资源的浪费: 使用时分复用系统传送计算机数据时&#xff0c;由于计算机数据的突发性质&#xff0c;用户对分配到的子信道的利用率一般是不高的。 二、 统计时分复用 STDM(Statistic TDM)

select distinct 语句详解

select distinct &#xff1a;当我们期望返回的数据不存在重复数据时&#xff08;每一行的数据都不一样&#xff09; 例如&#xff1a;表a select distinct a.* from a -- 可以查询出所有的信息 select distinct a.id from a -- 可以查询出id不同的信息&#xff0c;则还是全部…

【STM32】GPIO控制LED(HAL库版)

STM32最新固件库v3.5/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com) STM32最新固件库v3.5/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c 林何/STM32F103C8 - 码云 - 开源中国 (gitee.…

基于V/F控制的三相逆变器MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 参考文献&#xff1a;张飞,刘亚,张玉杰.基于V/F控制的三相逆变器仿真模型的研究[J].自动化与仪器仪表,2015 关于V/F控制的论文非常多&#xff0c;随意下载&#xff01; 当分布式电源经过逆变器运行于孤岛模…

【Java 进阶篇】Java Servlet 入门指南

Java Servlet 是一种用于构建Web应用程序的Java技术&#xff0c;它允许您处理HTTP请求和生成HTTP响应。本篇博客将向您详细介绍Servlet的入门知识&#xff0c;无论您是初学者还是有一定经验的开发者&#xff0c;都能受益匪浅。 什么是 Servlet&#xff1f; Servlet 是 Java 技…

18日草稿

AI视野今日CS.CV 计算机视觉论文速览 Tue, 17 Oct 2023 Totally 158 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers HairCLIPv2: Unifying Hair Editing via Proxy Feature Blending Authors Tianyi Wei, Dongdong Chen, Wenbo Zhou, Jing …

rocksdb db.h

编译 https://github.com/facebook/rocksdb/blob/main/INSTALL.md 如果您计划在生产中运行 RocksDB&#xff0c;请不要使用默认 make 或 make all 进行编译。这将以调试模式编译 RocksDB&#xff0c;这比发布模式慢得多。 默认存在centos7 build和run rocksdb的脚本 https://g…

Linux 函数调用的用户态与内核态

在用户态中&#xff0c;程序的执行往往是一个函数调用另一个函数。函数调用都是通过栈来进行的。 在进程的内存空间里面&#xff0c;栈是一个从高地址到低地址&#xff0c;往下增长的结构&#xff0c;也就是上面是栈底&#xff0c;下面是栈顶&#xff0c;入栈和出栈的操作都是…

适用于 Windows 10 和 Windows 11 设备的笔记本电脑管理软件

便携式计算机管理软件使 IT 管理员能够简化企业中使用的便携式计算机的部署和管理&#xff0c;当今大多数员工使用Windows 笔记本电脑作为他们的主要工作机器&#xff0c;他们确实已成为几乎每个组织不可或缺的一部分。由于与台式机相比&#xff0c;笔记本电脑足够便携&#xf…

pycharm使用运行Docker容器的python解释器

根据官方的介绍&#xff1a;pycharm使用docker中的python解释器&#xff0c;都是基于镜像来做 也就是说docker中的python解释器不能使用现有的docker容器&#xff0c;而是必须基于镜像重新构建专属的python环境 如果我们每个项目都需要添加一个相同的package就会导致需要重新构…

php危险函数及rce漏洞

php代码执行语句 eval() eval()语句 eval() 会将符合PHP 语法规范字符串当作php 代码执行。 <meta charset"UTF-8"> <pre><?php$dd$_REQUEST[dd];eval($dd);?>可以执行php代码 也可以套一层system执行系统操作指令 assert()函数 assert() …

基于利用协议模拟工具解决工控CTF题

概述 对于参赛者而言&#xff0c;工控CTF题目往往感觉很头疼&#xff0c;不知道如何下手&#xff0c;闲来之时&#xff0c;从网上看到一道协议分析的题目&#xff0c;想着用模拟工具试下&#xff0c;发现有意向不到的效果&#xff0c;本文中的小工具为开源工具&#xff0c;读者…

RCE 远程代码执行漏洞分析

RCE 漏洞 1.漏洞描述 Remote Command/Code Execute 远程命令执行/远程代码执行漏洞 这种漏洞通常出现在应用程序或操作系统中&#xff0c;攻击者可以通过利用漏洞注入恶意代码&#xff0c;并在受攻击的系统上执行任意命令。 2.漏洞场景 PHP 代码执行PHP 代码注入OS 命令执…

Android 系统架构

首语 由于工作内容的转变&#xff0c;使得我向Android系统方向转变&#xff0c;对于一个Android系统工程师&#xff0c;了解Android整个系统架构是必然的。本篇是Android系统学习的开篇&#xff0c;Android系统庞大且复杂&#xff0c;但是能对Android的认识更深&#xff0c;更…