mysql索引优化系列(二)

news2024/11/29 7:45:35

一、limit优化

之前的member会员表,联合索引为KEY `idx_name_age_address` (`name`,`age`,`address`),表里插入了十万条数据,一般情况下分页查询的sql语句:

select * from member limit 90000,10;
explain select * from member limit 90000,10;

执行计划是全表扫描,底层执行过程:首先,如果没有排序字段,默认按照id进行排序,查询前90010条件数据出来,然后去掉前面90000条件数据,最后剩下的10条就是查询结果,其实这种随着数据量越来越大,越往后面翻页,一次查询的数据只会多不会少,这样查询就会存在问题

1、根据自增主键排序

select * from member where id > 90000 limit 10;
explain select * from member where id > 90000 limit 10;

执行计划显示出使用了主键id索引的范围查询,这种情况,如果中间id有断层,查询的数据就会有问题;比如id为1的被删除了,理论上就会是id为90002的开始到90011结束,但是如果使用id筛选过滤的话就会有问题,具体体现如下(把id为1的数据删了):

select * from member limit 90000,10;
select * from member where id > 90000 limit 10;

2、根据普通索引字段排序分页查询

这个时候就需要对sql再做一些优化,根据二级索引b+树的结构,先根据字段排好序(一般情况下不会根据id排序),分页查出前10条数据的id,这样就可以避免回表而使用到了覆盖索引,接着再根据这些id关联查询出来的就是结果集

select * from member t1 inner join (select id from member order by name limit 90000,10) t2 on t1.id = t2.id;
explain select * from member t1 inner join (select id from member order by name limit 90000,10) t2 on t1.id = t2.id;

根据最左前缀的字段name进行排序分页查询的结果如下:

查询计划首先是id为2的先执行,类型为衍生查询,使用了覆盖索引,三个索引字段都用到了,并且使用了索引排序;第二执行是table为<derived2>执行,虽然是全表扫描,但是只有10个id结果集而已;第三执行的是table为t1的执行,查询类型为eq_ref,使用了主键关联,所以整个过程是相当快的

二、join优化

#创建表
CREATE TABLE `table1` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `a` int(11) DEFAULT NULL,
 `b` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#创建一个和table1一样的表
create table table2 like table1;

drop procedure if exists insert_table1;
CREATE PROCEDURE insert_table1 () BEGIN
	DECLARE
		i INT;
	
	SET i = 1;
	WHILE
			( i <= 100000 ) DO
			INSERT INTO table1 ( a, b )
		VALUES
			( i, i );
		
		SET i = i + 1;
		
	END WHILE;
END;


drop procedure if exists insert_table2;
CREATE PROCEDURE insert_table2 () BEGIN
	DECLARE
		i INT;
	
	SET i = 1;
	WHILE
			( i <= 100 ) DO
			INSERT INTO table2 ( a, b )
		VALUES
			( i, i );
		
		SET i = i + 1;
		
	END WHILE;
END;

call insert_table1();
call insert_table2();

 有两个表table1和table2,table1有100000条数据,table2有100条数据,索引字段也都是a,b就是普通字段

1、嵌套循环连接Nested-Loop Join(NLJ)算法

EXPLAIN select * from table1 t1 inner join table2 t2 on t1.a = t2.a;

执行过程:根据执行计划,首先执行的是table列为t2的,进行的是全表扫描;然后执行的是table为t1,是索引进行关联,table2是驱动表,table1是被驱动表;t2是索引关联查询,只是在索引树上查找到主键,然后回表一次,table2全表扫描一行数据,table1跟着全表扫描一行数据,执行过程我画了张草图

2、基于块的嵌套循环Block Nested-Loop Join(BNL)算法

EXPLAIN select * from table1 t1 inner join table2 t2 on t1.b = t2.b;

 mysql8.0之前执行的结果图

mysql8.0执行结果图

执行过程:table列值为t2的先执行,t2作为驱动表用的全表扫描,扫描了100行放到join_buffer中(放得下的情况,如放不下就分批),接着table1全表扫描100000行去join_buffer中进行数据匹配,匹配成功返回,下面的图是对这段话的说明:

show variables like '%join_buffer_size%';#查看join_buffer大小

 对于驱动表的定义:join连接数据量较少的表,也可以使用straight_join指定驱动表,但不适用于left join和right join,因为它们已经指定了驱动表,straight_join用法:

select * from table1 t1 straight_join table2 t2 on t1.b = t2.b;

假设字段无索引还使用了NLJ算法,总共就会扫描100000*100行,table2扫描一行数据到table1表遍历查询,因为没有走索引,这个过程是非常慢的,table2表每扫描一行就要去table1去遍历扫描,单次最坏的情况要十万次才找到结果集,所以就是100000*100次行扫描

一般情况下如果对性能有比较高的要求的系统,都不推荐使用join关联,如果非要用最好不要超过三个,优化起来比较困难,优化原理,小表驱动大表,join连接字段大表最好建有索引

三、in和exists

1、in的查询优化

小表驱动大表

#table1十万条数据 table2一百条数据
EXPLAIN select * from table1 where id in (select id from table2);#正确示例
EXPLAIN select * from table2 where id in (select id from table1);#错误示例

 table2是驱动表,走的是普通索引,因为id在二级索引树上就可以找到,所以选择普通索引;table1使用的是主键关联查询,直接就可以在主键索引树上获取数据;

 第二种可能是mysql做的优化,table2数据量比较少,虽然位置放的不合理,还是用table2作为驱动表,先进性table2的全表扫描,比之前的慢一点,之后的差不多

2、exists查询优化

如果能用in就不要用exists

explain select * from table2 t2 where exists (select * from table1 t1 where t1.id = t2.id);#正确示例
explain select * from table1 t1 where exists (select * from table2 t2 where t2.id = t1.id);#错误示例

 第一条sql,t2作为驱动表,虽然是全表扫描,但是数据量没有t1那么多,然后再用t1主键关联,这个和in位置写错了差不多

第二条sql,t1全表扫描,t1十万条数据,显然这个是很慢的,然后主键关联肯定时间也长

四、count查询优化

count(*),count(1),count(id),count(字段)

count的不同写法,要说快慢其实差不多,具体要分两种情况,一种是字段有索引,一种是字段无索引

1、有索引

count(*)≈count(1)>count(字段)>count(id)

count(*)的执行过程,底层不取值,按行累加

count(1)和count(字段)差不多,count(1)不需要取出字段值,就按照常量1做统计,count(字段还需要取值),count(1)要比count(字段要快)

count(字段)比count(id)要快,因为字段有索引情况下,二级索引比主键索引树数据少,扫描效率肯定快

2、无索引

count(*)≈count(1)>count(id)>count(字段)

上面已经说明count(*)和count(1),而count(id)和count(字段),没有索引自然count(id)要快,毕竟是在索引树上进行的内存操作

EXPLAIN select count(*) from member;
EXPLAIN select count(1) from member;
EXPLAIN select count(id) from member;
EXPLAIN select count(name) from member;

为什么count(id)也用到了二级索引,因为二级索引树的叶子节点数据量少,可能mysql底层也做了优化

3、优化方法

  • myisam类型表的总行数

myisam存储引擎的表会把总记录数维护在磁盘上

EXPLAIN select count(*) from test_myisam;

 innodb为什么不也设置一个总行数呢,因为myisam不支持事务,innodb需要支持事务,如果维护了,代价高

  • show table status
show table status like 'member';

 对member表的总行的估值

  • 将总行数维护到redis中

在数据删除和新增时,往redis中进行表key值加减,但是无法保证一致性

  • 增加表维护总行数

新增一个表table_count,存表明和这个表的行数,新增和删除时进行表table_count数据的更改,这样就可以事务的一致性了

五、mysql数据类型选择

1、数值类型

类型大小
TINYINT1字节
SMALLINT2字节
INT4字节
BIGINT8字节
FLOAT4字节
DOUBLE8字节
DECIMALDECIMAL(M,D) ,如果M>D,为 M+2否则为D+2

 如果整型没有负数,可以指定字段为无符号类型

对精度有要求,字段要设置成DECIMAL

2、时间类型

类型大小
DATE3字节
TIME3字节
YEAR1字节
DATETIME8字节
TIMESTAMP4字节

 如果只是日期,那就选date类型,这样可以减小筛选的粒度

datetime类型最大值是9999-12-31 23:59:59,而timestamp类型最大值是2038-01-19 03:14:07,衡量之后再做选择

3、字符串类型

类型大小
CHAR0-255字节
VARCHAR0-65535 字节
TINYBLOB0-255字节
TINYTEXT0-255字节
BLOB0-65535字节
TEXT0-65535字节

 如果已经确定了字符长度最好用char类型,比如char(4),而只存了一个a,就会在后面补空格,查询出来,mysql会帮你去空格,这样查询也会耗费性能;

如果是tinyint(4)就会进行0填充,加入存了一个2,结果就是0002,查询会去零,括号里的只是显示宽度

blob字段分开一个表存,只存id和blob内容就行,保证两个表的的双写一直就可以,这样可以减少主键索引的空间,提高查询速度

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

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

相关文章

MapReduce分布式计算(二)

MapReduce工作流程 原始数据File 1T数据被切分成块存放在HDFS上&#xff0c;每一个块有128M大小 数据块Block hdfs上数据存储的一个单元,同一个文件中块的大小都是相同的 因为数据存储到HDFS上不可变&#xff0c;所以有可能块的数量和集群的计算能力不匹配 我们需要一个动态…

ASP.NET Core MVC 从入门到精通之日志管理

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

向内核模块中添加新功能

一、向内核添加新功能 1.1 静态加载法&#xff1a; 即新功能源码与内核其它代码一起编译进uImage文件内 新功能源码与Linux内核源码在同一目录结构下 给新功能代码配置Kconfig #进入要添加的新功能的同级目录&#xff0c;这里用添加一个名为MY_HELLO的驱动做演示#cd到linux源…

C++基础(2)——函数高级

前言 本文主要介绍了C中函数高级相关的内容 3.1&#xff1a;函数默认参数 在函数定义的时候可以给形参赋初值&#xff0c;如果函数在调用的时候有传入参数&#xff0c;就使用传入的参数&#xff0c;如果没有就用默认的。 注意事项 1&#xff1a;如果某个参数有了默认值&…

Ubuntu下载速度过慢解决

今天用Ubuntu下载Roberta文件到本地&#xff0c;速度特别慢&#xff0c;Ubuntu 系统自带的源文件&#xff0c;都是国外的源网址&#xff0c;在国内下载安装升级源或者依赖的时候&#xff0c;都比较慢&#xff0c;更换国内的源地址&#xff0c;轻松搞定此问题。 目录 一、备份…

fecth

reducx巩固知识的传送门 1.向服务器发送数据请求的方案: 第一类:XMLHttpRequest 1.ajax:自己编写请求的逻辑和步骤 2.axios:第三方库&#xff0c;对XMLHttpRequest进行封装「基于promise进行封装」 第二类: fetchES6内置的API&#xff0c;本身即使基于promise&#xff0c;用全…

一文了解NAS协议原理

一文了解NAS协议原理 一、介绍二、NAS协议结构三、NAS协议工作原理四、NAS协议的安全机制4.1、NAS协议的认证过程4.2、NAS协议的加密过程 五、总结 一、介绍 NAS协议&#xff08;Network Attached Storage Protocol&#xff09;是一种用于网络附加存储设备&#xff08;NAS&…

学习HCIP的day.16

目录 扩展知识点 一、端口镜像 SPAN 二、DHCP 三、DHCP snooping 四、端口安全 五、SSH 六、端口隔离 扩展知识点 一、端口镜像 SPAN [r1]observe-port interface GigabitEthernet 0/0/2 监控接口[r1]interface GigabitEthernet 0/0/0[r1-GigabitEthernet0/…

浅谈单元测试的那些事

Part 01 什么是单元测试 单元测试是一种软件测试方法&#xff0c;用于测试软件系统的最小可测试单元&#xff0c;例如函数、方法或类的行为。单元测试通常由开发人员编写&#xff0c;并在编写代码时就开始执行。这样可以保证实时检测代码中的错误、缺陷和潜在的问题&#xff0…

打包Java程序为.exe文件

文章目录 将Java程序打包成Jar包打包为.exe文件效果展示文件检索联系作者 ​&#x1f451;作者主页&#xff1a;Java冰激凌 接上篇 我们还未介绍到如何打包为一个.exe文件 将Java程序打包成Jar包 1. file -> Project Structure 或者直接使用全局快捷键&#xff08;Ctrl Al…

Java调用ChatGPT实现可连续对话和流式输出

目录 1. 配置阶段1.1 依赖引入1.2 配置application.yml文件1.3 注解添加 2. 使用2.1 生成回答2.1.1 测试 2.2 生成图片2.2.1 测试 2.3 下载图片2.3.1 测试 2.4 生成流式回答2.4.1 流式回答输出到IDEA控制台2.4.2 流式回答输出到浏览器页面2.4.3 流式回答结合Vue输出到前端界面 …

Docker: 改变容器化世界的革命性技术

目录 1.1什么是虚拟化 1.2什么是Docker 1.3容器与虚拟机的比较 1.4Docker组建 2、Docker安装 2.2设置ustc的镜像 2.3Docker的启动与停止 3、docker常用命令 3.1镜像 3.2容器相关命令 1.1什么是虚拟化 在计算机中&#xff0c;虚拟化&#xff08;Vitualization&#x…

这可能是最全的Web测试各个测试点,有这一篇就够了

前言 什么是Web测试&#xff1f; Web测试测试Web或Web应用程序的潜在错误。它是在上线前对基于网络的应用程序进行完整的测试。 Web测试检查 功能测试 易用性测试 接口测试 性能测试 安全测试 兼容性测试 1、功能测试 测试网页中的所有链接、数据库连接、网页中用于提交或从…

结合具体代码理解yolov5-7.0锚框(anchor)生成机制

最近对yolov5-7.0的学习有所深入&#xff0c;感觉官方代码也比较易读&#xff0c;所以对网络结构的理解更进一步&#xff0c;其中对锚框生成这块没太看明白细节&#xff0c;也想弄明白这块&#xff0c;于是前前后后好好看了代码。现在把我的学习收获做一下记录。个人见解&#…

如何在大规模服务中迁移缓存

当您启动初始服务时&#xff0c;通常会过度设计以考虑大量流量。但是&#xff0c;当您的服务达到爆炸式增长阶段&#xff0c;或者如果您的服务请求和处理大量流量时&#xff0c;您将需要重新考虑您的架构以适应它。糟糕的系统设计导致难以扩展或无法满足处理大量流量的需求&…

第三章 decimal模块

1. decimal 模块介绍 decimal 模块是 Python 提供的用于进行十进制定点和浮点运算的内置模块。使用它可以快速正确地进行十进制定点和浮点数的舍入运算&#xff0c;并且可以控制有效数字的个数。 使用 decimal 模块主要是因为它与 Python 自带的浮点数相比&#xff0c;有以下…

关于Stream流和Lambda表达式,这些技巧你都知道吗?

&#x1f4a7; 关于 S t r e a m 流和 L a m b d a 表达式&#xff0c;这些技巧你都知道吗&#xff1f; \color{#FF1493}{关于Stream流和Lambda表达式&#xff0c;这些技巧你都知道吗&#xff1f;} 关于Stream流和Lambda表达式&#xff0c;这些技巧你都知道吗&#xff1f;&…

2014年全国硕士研究生入学统一考试管理类专业学位联考写作试题

2014年1月真题: 四、写作:第56~57小题&#xff0c;共65分。其中论证有效性分析30 分&#xff0c;论说文35分。 56.论证有效性分析: 分析下述论证中存在的缺陷和漏洞&#xff0c;选择若干要点&#xff0c;写一篇600字左右的文章&#xff0c;对该论证的有效性进行分析和评论。…

马克思第二章

1.实践和认识 实践决定认识&#xff0c;认识又反作用于实践 实践的特点&#xff1a; 1.直接现实性 2.自觉能动性 3.社会历史性 实践和认识的关系 1.实践是认识的来源 2.实践是认识的目的 3.实践是认识的发展动力 4.实践是检验认识真理的唯一标准 5.认识又反作用于实践&#xf…

gocv Windows10下编译和安装(opencv4.7)

opencv居然还没有官方的golang版&#xff0c;出乎意料。为了编译安装这玩意&#xff0c;折腾了一下午&#xff0c;记录下&#xff1a; 资源提前下载 1、 MinGW-w64 这里的坑是对于只懂一点点的人&#xff0c;容易选错版本&#xff1a; 没仔细看的人很可能会选win32的&#x…