MySQL — SQL 优化

news2024/11/19 14:41:19

文章目录

  • SQL 优化
  • 一、插入数据
  • 二、主键优化
    • 2.1 数据组织方式
    • 2.2 页分裂
    • 2.3 页合并
    • 2.4 主键设计原则
  • 三、 Order by 优化
    • 3.0 排序方式讲解
    • 3.1 升序/降序联合索引结构图示
    • 3.2 总结
  • 四、Group by优化
  • 五、limit优化
  • 六、 count优化
  • 七、update优化
  • 七、update优化

SQL 优化

一、插入数据

insert into tb_test values(1,'tom');
insert into tb_test values(2,'cat');
insert into tb_test values(3,'jerry');
......

如果我们一次性往数据库中插入多条记录,可以从下面几个方面进行优化

  • 批量插入数据

不建议超过1000条

Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
  • 手动控制事务
start transaction;

insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');

insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');

insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');

commit;
  • 逐渐顺序插入

​ 性能高于乱序插入

主键乱序插入 : 8 1 9 21 88 2 4 15 89 5 7 3
主键顺序插入 : 1 2 3 4 5 7 8 9 15 21 88 89
  • 大批量插入数据 - Load指令

​ 如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入

-- 客户端连接服务端时,加上参数 -–local-infile
mysql –-local-infile -u root -p

-- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;

-- 执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n' ;

案例

​ 我们使用load插入数据的时候尽量也按照主键顺序插入,因为性能比较高

查看参数“local_infile”值

select @@local_infile;

创建表结构

CREATE TABLE `tb_user2` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`password` VARCHAR(50) NOT NULL,
`name` VARCHAR(20) NOT NULL,
`birthday` DATE DEFAULT NULL,
`sex` CHAR(1) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_user_username` (`username`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 ;

加载数据

fields terminated by ‘,’ 每个字段以逗号分隔

lines terminated by ‘\n’ ; 每一行以\n分隔

load data local infile 'D:\\tb_sku1.sql' into table tb_user2 fields terminated by ',' lines terminated by '\n' ;

插入两百万条数据大约五十秒。

image-20230525141055559

二、主键优化

主键顺序插入的性能是要高于乱序插入的,这块来讲解为什么

2.1 数据组织方式

​ 在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表

​ 如下所示,聚集索引下叶子结点存放整条数据,“6”对应的是id为6的整条数据

image-20230525142342087

逻辑存储空间

image-20230525142813948

2.2 页分裂

页可以为空,也可以填充一般,也可以填充100%。

​ 每个页包含了2-N行数据(如果一行数据多大,会行溢出),根据主键排列

主键顺序插入页的情况

​ 如下图所示,从左到右依次添加

image-20230525143405288

逐渐乱序插入页的情况

如下所示,有两个页,但是都已经写满了

image-20230525144039581

如果我们再想插入一个id为50的数据,应该添加在47后面,但是页1和页2已经满了,此时就会开启一个新的数据页,id为50的数据不会直接写到页3上 。

我们在页1上找到百分之五十的位置,那23,47是在百分之五十位置之外的,将其移动到第三张页,然后将id=50的数据页插入到这张页

image-20230525144613220

​ 之后页的指针也应该发生变化,页1不指向页2而是指向页3,而这种现象称为页分裂

image-20230525144723483

2.3 页合并

当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除,并且他的空间允许被其他记录声明使用

当业中删除的记录达到了MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近页(前或后)看看是否可以将两个页合并并以优化空间使用

​ MERGE_THRESHOLD 合并页的阀值,可以自己设置,在创建表或者创建索引时指定

​ 如下所示,页2中删除的数据达到了百分之五十

image-20230525151252321

​ 那就会看看靠近的页,能否合二为一,页1显然不能,页2的数据可以,合并完成后,便是下面的样子

image-20230525151426035

​ 如果我们插入一个id=20的数据,会直接插入到页3

image-20230525151514205

2.4 主键设计原则

  • 满足业务需求的情况下,尽量降低主键的长度

​ 主键索引只有一个,但是二级索引可能有很多个,在二级索引的叶子结点中挂的就是主键,如果主键长度比较长,二级索引比较多,那占用的磁盘的位置也比较多

  • 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键

​ 如果不是顺序插入数据,很有可能出现页分裂现象

  • 尽量不要使用UUID做主键或者是其他自然主键,如身份证号

​ 因为每次生成的UUID是无序的,如果插入数据库的时候就会乱序插入

  • 业务操作时,避免对主键的修改

​ 因为修改主键会影响索引结构

三、 Order by 优化

将我们之前phone、name所设置的索引删除

drop index idx_user_phone on tb_user;
drop index idx_user_phone_name on tb_user;
drop index idx_user_name on tb_user;

3.0 排序方式讲解

MySQL两种排序方式

  • Using filesort

​ 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序

​ 下面的排序并没有通过索引

image-20230525154459705

​ 下面的排序也没有通过索引

image-20230525154551185

  • Using index

通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。

​ 创建联合索引

create index idx_user_age_phone on tb_user(age,phone);

​ 下面再来测试两个sql语句

explain select id,age,phone from tb_user order by age ;

explain select id,age,phone from tb_user order by age, phone ;

​ 发现是Using Index

image-20230525162009925

案例

  • 在看一下倒序排序
explain select id,age,phone from tb_user order by age desc , phone desc ;

​ 成功的使用了索引

​ Backward index scan; 表示反向使用索引

image-20230525162900858

  • 刚刚我们创建索引是先age,再phone,那我们此时排序先按phone进行排序,再按age进行排序

Using filesort: 出现这个字符的原因是违背最左前缀法则的,因为我们在创建索引的时候age是第一个字段,phone是第二个字段,但是我们排序的时候,phone是第一个字段,age是第二个字段,所以这个地方phone没有走索引,age走了索引

explain select id,age,phone from tb_user order by  phone ,age ;

image-20230525170902685

  • age Asc ,phone Desc

​ 我们创建age,phone联合索引的时候,并没有指定升序排列还是降序排列,那就默认升序

​ 但是这个地方我们的phone是倒序排列,所以就需要额外的排序,导致了Using filesort

explain select id,age,phone from tb_user order by age Asc ,phone Desc ;

image-20230525171831956

这个地方也是可以优化的,如下所示

create index idx_user_age_phone_ad on tb_user(age asc ,phone desc);

3.1 升序/降序联合索引结构图示

叶子结点

如果排序符合下面的情况,直接返回就可以了,不需要再排,所以走索引的排序会比较快,效率比较高

image-20230525172940016

所有的排序规则都有一个条件,就是使用了覆盖索引

MySQL——存储引擎于索引应用

比如下面的数据,id,age,phone在叶子结点中都存在,不需要回表查询

explain select id,age,phone from tb_user order by age Asc ,phone Desc ;

3.2 总结

上面案例能够正常使用索引排序的情况

创建索引、联合索引的时候不指定升序排列还是降序排列,默认就是升序排列

  • order by age
  • order by age,phone
  • order by age Desc, phone Desc

一个升序,一个降序会出现filesort现象,但是也可以优化,如下所示

create index idx_user_age_phone_ad on tb_user(age asc ,phone desc);

总结

  • 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
  • 尽量使用覆盖索引。
  • 多字段排序, 一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
  • 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)。

四、Group by优化

  • 在分组操作时,可以通过索引来提高效率
  • 分组操作时,索引的使用也是满足最左前缀法则的

将之前的索引全部删除

drop index idx_user_pro_age_sta on tb_user;
drop index idx_email_5 on tb_user;
drop index idx_user_age_phone_aa on tb_user;
drop index idx_user_age_phone_ad on tb_user;
explain select profession , count(*) 
from tb_user 
group by profession ;

Extra:Using temporary 性能是比较低的

image-20230525204940944

创建索引进行优化

​ 尽量创建联合索引

create index idx_pro_age_status on tb_user(profession,age,status);

​ 此时变成了Using index

image-20230525205204663

如果此时根据age进行分组呢?

explain select age , count(*) 
from tb_user 
group by age ;

用到了索引但是页使用了临时表,效率比较低.

​ 因为根据age分组不满足最左前缀原则,出现了临时表

image-20230525205456027

根据profession、age分组呢?

explain select profession, age , count(*) 
from tb_user 
group by  profession,age ;

​ 满足最左前缀原则,很完美

image-20230525205744944

profession过滤,age分组呢?

explain select  age , count(*) 
from tb_user 
where profession = '软件工程'
group by  age ;

image-20230525210255599

五、limit优化

​ 在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低。

一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

​ 不清楚覆盖索引的可以查看下面这一篇文章:

MySQL——存储引擎于索引应用

首先说明 MySQL不支持 where id in ( … limit …) 这种语法,但是我们可以使用下面这种形式的。

explain select * 
from tb_sku t , (select id from tb_sku order by idlimit 2000000,10) a 
where t.id = a.id;

六、 count优化

如果数据量很大,在执行count操作时,是非常耗时的。

  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高; 但是如果是带条件的count,MyISAM也慢

  • InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

    优化思路:手动技术,借用Redis等缓存技术,插入数据时加1,删除数据时减一

​ **count() **是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是NULL,累计值就加 1,否则不加,最后返回累计值

​ count(主键) 需要取值, count(字段) 需要进行判断,count(数字)需要累加,而count(*) 效率最高,不取值直接在服务层按行进行累加

效率排名 count(字段) < count(主键 id) < count(1) ≈ count(*)

count用法含义
count(主键)InnoDB 引擎会遍历整张表,把每一行的 主键id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)
count(字段)没有not null 约束 : InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。有not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加(省略了判断是否为NULL)。
count(数字)InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加。
count(*)InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加

比如

count(1) : InnoDB会遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加

​ 下面四个结果相同

select count(1) from tb_user ;
select count(0) from tb_user ;
select count(-1) from tb_user ;
select count(2) from tb_user ;

七、update优化

当我们在执行更新的SQL语句时,会锁定id为1这一行的数据,然后事务提交之后,行锁释放。

update course set name = 'javaEE' where id = 1 ; 

​ 但是当我们在执行如下SQL时。当我们开启多个事务,在执行上述的SQL时,我们发现行锁升级为了表锁。 导致该update语句的性能大大降低。

因为name字段没有索引,加的不是行锁而是表锁,当表锁住后,其他会话无法执行update等语句
update course set name = 'SpringBoot' where name = 'PHP' ; 

优化方法

  • 尽量使用主键修改
  • 添加索引,尽量使用有索引表示的字段修改

总的来说就是根据索引字段进行更新,否则就会出现行锁升级为表锁,影响并发执行的效率

下面四个结果相同

select count(1) from tb_user ;
select count(0) from tb_user ;
select count(-1) from tb_user ;
select count(2) from tb_user ;

七、update优化

当我们在执行更新的SQL语句时,会锁定id为1这一行的数据,然后事务提交之后,行锁释放。

update course set name = 'javaEE' where id = 1 ; 

​ 但是当我们在执行如下SQL时。当我们开启多个事务,在执行上述的SQL时,我们发现行锁升级为了表锁。 导致该update语句的性能大大降低。

因为name字段没有索引,加的不是行锁而是表锁,当表锁住后,其他会话无法执行update等语句
update course set name = 'SpringBoot' where name = 'PHP' ; 

优化方法

  • 尽量使用主键修改
  • 添加索引,尽量使用有索引表示的字段修改

总的来说就是根据索引字段进行更新,否则就会出现行锁升级为表锁,影响并发执行的效率

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

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

相关文章

【MySQL 数据库】5、存储引擎

目录 一、MySQL 体系结构二、存储引擎简介三、InnoDB 存储引擎四、MyISAM五、Memory六、三大存储引擎比较七、存储引擎的选择 一、MySQL 体系结构 连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主…

07:MYSQL----多表查询

目录 1:多表查询概述 2:多表查询分类 3:内连接 3:外连接 4:自连接 5:联合查询-union&#xff0c;union all 6:子查询 1:多表查询概述 select * from emp , dept; emp:表中有6条数据, dept表中有5条数据只查询出来的数据为:30条 概述:指从多张表中查询数据 笛卡尔积…

在vite或者vue-cli中使用.env[mode]环境变量

在项目中总会遇到一些默认的配置,需要我们配置到静态文件中方便我们去获取,这时候就可以用到这个.env环境变量文件,在cli创建的项目中顶层的nodejs会有一个process对象,这个对象可以根据不同的环境获取不同的环境配置文件,但是vite中获取变量的方式不一样。 创建变量文件.env.…

如何编写接口自动化框架系列之requests详解(三)

目录 1.http协议 2.requests介绍 3.requests的主要功能 3.requests的主要功能 3.1 场景1-常用方法 3.2 场景2-通用方法 3.3 场景3-cookies认证方式 4.requests 在项目中的实践 4.1 在接口层实现一个接口 4.2 在测试用例层调用 4.3 项目总结 本文是接口自动化测试框架…

IOC初始化 IOC启动阶段 (Spring容器的启动流程)

[toc](IOC初始化 IOC启动阶段 (Spring容器的启动流程)) IOC初始化 IOC启动阶段 (Spring容器的启动流程) Resource定位过程&#xff1a;这个过程是指定位BeanDefinition的资源&#xff0c;也就是配置文件&#xff08;如xml&#xff09;的位置&#xff0c;并将其封装成Resource对…

Makefile基础教程(make的隐式规则)

文章目录 前言一、什么是make的隐式规则二、makefile中出现同名目标时三、一些常见的隐式规则四、查看隐式规则五、隐式规则缺点六、禁用隐式规则1.全局禁用2.局部禁用 总结 前言 本篇文章将给大家介绍make的隐式规则。 一、什么是make的隐式规则 Make 的隐式规则是指 Make …

css选择器及其权重

1. 类型选择器 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

【ZYNQ】裸机 PS + PL 双网口实现之 ZYNQ 配置

目前&#xff0c;在 ZYNQ 中进行以太网开发的方案&#xff0c;大部分都是基于通过 PS 的 MIO 以 RGMII 接口连接外部 PHY 芯片的方式。但是&#xff0c;由于使用 PS 的 MIO 只能以 RGMII 接口连接外部 PHY 芯片&#xff0c;这就限制了支持其他接口 PHY 芯片的使用&#xff0c;如…

分文件实现温湿度数据管理系统项目

目标&#xff1a; 了解分文件的概念&#xff0c;要依次从C语言的函数声明、变量的存储类别、C语言编译预处理&#xff0c;说起。这些知识点我们之前或多或少接触过&#xff0c;这里做个总结与拓展。经过总结&#xff0c;最后我们归纳出一个实现C语言模块化编程的技巧&#xff…

03-bootstrap-响应式布局-栅格系统

一、概述 1、栅格系统是 Bootstrap 中响应式布局的重要组成部分&#xff0c;旨在实现页面元素的自适应排版。Bootstrap 栅格系统将屏幕宽度分为 12 列&#xff0c;通过在 HTML 元素上添加相应的类名&#xff0c;可以让元素占据指定数量的列数&#xff0c;从而实现灵活的布局效…

5种易实现的Linux和 Windows VPS速度提升方法

​  无论是Linux VPS还是Windows VPS&#xff0c;网站速度的提高都是非常重要的。它们在提高网站速度方面都有很多的优化方法。下面我们将介绍 5 种提高网站速度的方法。 1.通过缓存加速 缓存通常是用来加快商业网站加载时间的技术&#xff0c;因此它也可以用在 VPS 上。没有…

车架号查车辆信息-vin查车辆信息api接口

接口地址&#xff1a; https://登录后显示/pyi/88/264(支持:http/https)) 在线查询&#xff1a;https://www.wapi.cn/car_vin.html 网站地址&#xff1a;https://www.wapi.cn 返回格式&#xff1a;json,xml 请求方式&#xff1a;GET,POST 请求说明&#xff1a; Md5验证方式-…

字符串、字符串列表,倒序生成字典。

带数字的字符串以数字为key倒序生成字典&#xff0c;字符串列表按其元素索引为key倒序生成字典。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简…

【MySQL】-- 表的操作

目录 表的操作 创建表 创建表案例 查看表结构 查看表结构案例 查看历史上表的创建语句 修改表 修改表实例 新增列属性 修改列属性 删除列属性 修改列名 修改表名 删除表 表的操作 创建表 语法&#xff1a; CREATE TABLE (if not exists) table_name (fie…

【MyBatisPlus框架】

文章目录 MyBatisPlus1.概述1.1 简介1.2特性1.3支持数据库1.4框架结构 2.入门案例2.1 创建数据库以及表2.2 创建工程2.2.1引入依赖 2.3编写代码 3.基本CRUD3.1BaseMapper3.2插入3.3删除3.4修改3.5查询3.6通用Service 4.常用注解4.1TableName4.1.1问题4.1.2通过TableName解决上述…

简述springmvc的流程

4、SpringMVC的执行流程 用户向服务器发送请求&#xff0c;请求被SpringMVC 前端控制器 DispatcherServlet捕获。 DispatcherServlet对请求URL进行解析&#xff0c;得到请求资源标识符&#xff08;URI&#xff09;&#xff0c;判断请求URI对应的映射&#xff1a; a) 不存在 …

day05 java_Spring IoC 和 DI

为什么使用spring框架 1.解耦代码(每次使用都要new一个对象) 2.解决事务繁琐问题(创建对象----初始化----调用方法销毁对象) 3.使用第三方框架麻烦的问题 总结:spring是一个轻量级的Ioc,Di和AOP容器 轻量级:简洁,高效,低依赖 **容器:**创建对象并将对象存储对象,同时管理…

高矿化度矿井水深度除氟装置CH-87技术解析

高矿化度矿井水是指含有高浓度溶解性矿物质的废水&#xff0c;通常指的是含有高浓度钠、钙、镁、铁、铝、钾等离子的废水。这些离子通常来自于废水所处的环境、工业或生产过程中使用的原材料和化学品。高矿化度的废水通常具有高盐度、高电导率、高硬度等特征&#xff0c;对环境…

性能测试计划不会写?我告诉你有模板你看不看

目录 1 简介 2 测试进入条件 3 测试退出条件 4 性能测试需求 5 测试风险 6 测试时机 7 测试策略 8 测试资源 9 测试进度 10 交付物 1 简介 1.1 目的 【描述性能测试计划的目的。】 1.2 背景 【描述项目或产品的背景。】 1.3范围 【描述性能测试的整体范围。】 2 测试进入条件 【…

Java如何配置环境变量

Java如何配置环境变量 0. 前言1. 下载Java2. 配置环境变量2.1新建 Java_Home2.2 编辑Path情况1情况2 3. 验证安装 0. 前言 本节记录如何配置Java环境变量&#xff0c;用自己重装过的系统实操 操作系统&#xff1a;Windows10 专业版 Java版本&#xff1a;jdk1.7.0_07 1. 下载…