当前位置: 首页 > news >正文

[MySQL数据库] SQL优化

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (93平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(97平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(95平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482
🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1. 插入数据
    • 1.1 Insert
    • 1.2 大批量插入数据
  • 2. 主键优化
  • 3. order by优化
  • 4. group by优化
  • 5. limit优化
  • 6. count优化
    • 6.1 概述
    • 6.2 count用法
  • 7. update优化

1. 插入数据

1.1 Insert

如果我们需要一次性往数据库表中插入多条数据,可以从下面三个方面进行优化.

insert into tb_test values(1,'tom');
insert into tb_test values(2,'cat');
insert into tb_test values(3,'jerry');
.....
  1. 优化方案一:
    批量插入数据
Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
  • 减少网络开销,单批次插入比多批次插入减少了客户端和服务器之间的往返通信.
  • 降低SQL解析成本,MySQL只需要在服务层解析一次SQL语句,而不是多次.
  • 减少日志写入,InnoDB的事务日志(redo log)可以批量写入
  1. 优化方案二:
    手动控制事务
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;
  • 减少事务开销,如果不是手动提交的话,默认每条语句都是一条事务,这样就会导致不停的刷盘写日志的操作.批量操作可以减少磁盘IO的压力.
  • 减少锁的持有时间,表锁和行锁在整个事务中只需要获取一次.
  • 总的来说: 自动提交,每条语句都会占用一个完整的事务开销,多条Insert会共享一个事务的开销.
  1. 优化方案三:
    主键顺序插入,性能要高于乱序插入:
主键乱序插入 : 8 1 9 21 88 2 4 15 89 5 7 3 
主键顺序插入 : 1 2 3 4 5 7 8 9 15 21 88 89

优化方案三的具体原因我们下面会介绍.

1.2 大批量插入数据

如果一次性需要插入大批量数据.使用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' ; 

2. 主键优化

在上面,我们提到,主键顺序插入的性能要高于乱序插入,下面,我们就来具体介绍一下原因,然后分析一下主键应该如何设计.

  1. 数据组织方式
    在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表
    在这里插入图片描述
    行数据,都是存储在聚集索引的叶子结点上的,而我们之前也讲过InnoDB的逻辑结构图:
    在这里插入图片描述
    在InnoDB引擎中,数据行时记录在逻辑结构page页中的,而每个页的大小是固定的,默认16KB.那也就意味着,一个页中所存储的行也是有限的,如果插入的行数据row在该页中存储不小,将会存储到下一个页中,页与页之间会通过指针连接.
  2. 页分裂
    页可以为空,也可以填充一半,也可以填充100%,每个页包含了2-N行数据,根据主键排列.
  • 主键顺序插入效果
    从磁盘中申请页,主键顺序插入
    在这里插入图片描述
    第一个页没有满,继续往第一页插入
    在这里插入图片描述
    当第一个页写满之后,再写入第二个页,页与页之间会通过指针连接
    在这里插入图片描述
    当第二个页写满之后,再往第三页写入
    在这里插入图片描述
  • 逐渐乱序插入效果
    第一页和第二页都已经写满了,存放如图所示的数据
    在这里插入图片描述
    此时再插入id为50的记录,我们来看看会发生什么现象,会再次开启一个页,写入新的页中吗?
    在这里插入图片描述不会,因为,索引结构的叶子结点是有顺序的,按照顺序,应该存储在47之后.
    在这里插入图片描述
    但是47所在的第一页已经满了,存储不了50对应的数据了,那么此时会开辟一个新的页.
    在这里插入图片描述
    但是并不会直接将50存入第三页,而是会将第一页的一半数据移动到第三页,然后在第三页插入50.

在这里插入图片描述
在这里插入图片描述
移动数据,并插入id为50的数据之后,那么此时,这三个页之间的数据顺序是有问题的.第一页的下一个页应该是第三页,第三页的下一页应该是第二页,所以此时需要重新设置链表指针.
在这里插入图片描述
上述的这种现象,称之为"页分裂",是比较耗费性能的操作.
3. 页合并
目前表中已有数据的索引结构(叶子结点)如下:
在这里插入图片描述
当我们对已有的数据进行删除的时候,具体的效果如下:
当删除一行记录时,实际上并没有被物理删除,只是被标记为删除并且它的空间变得允许被其他的记录声明使用.
在这里插入图片描述
当我们继续删除第二页的数据记录
在这里插入图片描述当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用.
在这里插入图片描述

在这里插入图片描述
删除数据,并将页合并之后,再次插入新的数据21,则直接插入第三页.

在这里插入图片描述
这里面所发生的合并也的这个现象,就称之为"页合并".
4. 索引设计原则

  • 满足业务需求的情况下,尽量降低主键的长度.迫不得已的时候可以用其他的,比如分布式系统,可以选择使用雪花算法.
  • 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键.
  • 尽量不要使用UUID做主键或者是其他的自然主键,避免出现页分裂的现象,比如身份证号.
  • 业务操作时,避免对主键的修改.

3. order by优化

MySQL的排序,有两种方式:
Using filesort: 通过表的索引或者全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回的排序结果都叫FileSort排序.
Using index: 通过有序索引扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高.
对于以上的两种排序方式,using index的性能高,而using filesort的性能很低,我们在优化排序操作的时候,尽量要优化为using index.
接下来,我们来做一个测试:

  1. 准备数据
    把之前为tb_user表所建立的部分索引直接删除掉
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;

在这里插入图片描述
2. 执行排序SQL

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

在这里插入图片描述

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

在这里插入图片描述
由于age,phone都没有索引,所以此时再排序时,出现using filesort,排序性能较低.

  1. 创建索引
-- 创建索引
create index idx_user_age_phone_aa on tb_user(age,phone);
  1. 创建索引之后,根据age,phone进行升序排序
explain select id,age,phone from tb_user order by age;

在这里插入图片描述

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

在这里插入图片描述
建立索引之后,再次进行排序查询,就由原来的using filesort,变为了using index,性能就比较高了.

  1. 创建索引之后,根据age,phone进行降序排序.
explain select id,age,phone from tb_user order by age desc , phone desc ;

在这里插入图片描述
也出现了using index,但是此时extra出现了backward index scan,这个代表反向扫描索引,因为在MySQL中我们创建的索引,默认索引的叶子结点是从小到大排序的,而此时我们查询排序时,是从大到小,所以在扫描时,就是反向扫描,就会出现backward index scan.在MySQL8版本中,支持降序索引,我们也可以创建降序索引.

  1. 根据age,phone进行排序,一个降序,一个升序.
explain select id,age,phone from tb_user order by age asc , phone desc ;

在这里插入图片描述
因为创建索引时,如果未指定顺序,默认都是按照升序排序的,而查询时,一个升序,一个降序,此时就会出现using filesort.
在这里插入图片描述
为了解决上述的问题,我们可以创建一个索引,这个联合索引中age升序排序,phone倒序排序.

  1. 联合创建索引(age升序排序,phone倒序排序)
create index idx_user_age_phone_ad on tb_user(age asc ,phone desc);

在这里插入图片描述
8. 然后再次执行如下sql

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

在这里插入图片描述
升序,降序联合索引结构如图所示:
在这里插入图片描述
在这里插入图片描述
总结: 由上上述原则,我们可以得出order by优化原则:

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

4. group by优化

分组操作,我们主要来看索引对分组操作的影响.首先我们将tb_user表中的索引全部删除.
在这里插入图片描述
接下来,在没有索引的情况下,执行如下sql,查询执行计划:

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

在这里插入图片描述
然后,我们在针对与profession,age,status创建一个联合索引.

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

紧接着,再执行前面相同的sql查看执行计划:

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

在这里插入图片描述
再执行如下分组查询sql,查看执行计划:
在这里插入图片描述
在这里插入图片描述
我们发现,如果仅仅根据age分组,就会出现using temporary,而如果是根据profession,age两个字段同时分组,则不会出现using temporary.原因是因为对于分组操作,在联合索引中,也是符合最左前缀法则的.如果分组中跳过了联合索引中的其中一个字段时,后面字段的索引都会失效.
所以在分组操作中,我们需要通过以下两点进行优化,以提升性能:

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

5. limit优化

在数据量比较大,如果进行limit分页查询,在查询时,越往后,分页查询效率也越低.
我们一起来看看limit分页查询耗时对比:
在这里插入图片描述
通过测试我们会看到,越往后,分页查询效率越低,这就是分页查询的问题所在.
因为,当在进行分页查询时,如果执行limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000 - 2000010的记录,将其他记录丢弃,查询的代价非常大.

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

6. count优化

6.1 概述

select count(*) from tb_user ;

在之前的测试中,我们发现,如果数据量很大,在执行count操作的时候,非常耗时.

  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高.
  • InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数.

如果要说大幅提升InnoDB表的count效率,主要的优化思路: 自己计数(可以借助Redis这样的数据库进行).

6.2 count用法

count()是一个聚合函数,对于返回的结果集,一行一行的判断,如果count函数参数不是null,累计值就加1,否则不加,最后返回累计值.
用法:count(*)、count(主键)、count(字段)、count(数字)

在这里插入图片描述
按照效率排序的话: count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽量使用 count(*)

7. update优化

我们主要需要注意一下update语句执行时的注意事项.

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

当我们在执行更新的sql语句时,会锁定id为1这一行数据,然后事务提交之后,行锁释放.
但是当我们开启多个事务,在执行上述的sql时,我们发现行锁升级为了表锁,导致该update的语句性能大大降低.
InnoDB的行锁是针对索引加的锁,而不是对记录加的锁,并且该索引不能失效,否则就会从行锁自动升级为表锁.

相关文章:

  • MyBatis-Plus 常用用法总结
  • 电脑自带画图工具,提取颜色
  • 软考软件评测师——计算机组成与体系结构(CPU指令系统)
  • 脚本语言Lua
  • std::deque 底层实现结构
  • 组合问题(去重)
  • 【Java】Spring的声明事务在多线程场景中失效问题。
  • 欧拉计划 Project Euler 73(分数有范围计数)题解
  • 第七节第三部分:从JDK8开始接口新增的方法、接口的多继承、注意事项
  • 产品方法论与 AI Agent 技术的深度融合:从决策智能到价值创造
  • 串口共用错误排查指南(2025年5月15日 09:25)
  • AI大模型应用:17个实用场景解锁未来
  • 全国青少年信息素养大赛 Python编程挑战赛初赛 内部集训模拟试卷九及详细答案解析
  • 特种设备事故背后,叉车智能监控系统如何筑牢安全防线
  • 深度学习中--模型调试与可视化
  • 【电子通识】热敏纸的静态发色性能和动态发色性能测试方法
  • 考研408《计算机组成原理》复习笔记,第二章(2)数值数据的表示(浮点数篇)
  • Cursor vs VS Code vs Zed
  • leetcode0621. 任务调度器-medium
  • Linux记录
  • 上百家单位展示AI+教育的实践与成果,上海教育博览会开幕
  • 中央提级巡视后,昆明厅官郭子贞接受审查调查
  • 陕西三原高新区违法占用土地,被自然资源局罚款10万元
  • 普京调整俄陆军高层人事任命
  • 科技部等七部门:优先支持取得关键核心技术突破的科技型企业上市融资
  • 首次采用“顶置主星+侧挂从星”布局,长二丁“1箭12星”发射成功