SQL优化详解

devtools/2024/9/23 4:25:13/

目录

·插入数据

insert的优化(少量数据)

批量插入

手动事务提交

主键顺序插入

插入大量数据

·主键优化

数据组织方式:

页分裂:

主键顺序插入的方式:

主键乱序插入:

页合并:

主键设计原则:

·order by优化

·group by 优化

·limit优化

优化方式:

·count优化

count的用法:

几种用法的优劣:

count(主键)

count(字段)

count ( 1 )

count (* )

·update优化


·插入数据

插入多条数据的优化方案:(插入一条没啥优化的哈)

insert的优化(少量数据)

批量插入

像这种模式,一般建议数据量最多在500-1000条左右。

Insert into tb_test values(1,'To'),(2,'WO'),(3,'jee');

手动事务提交

因为每句insert的执行都会涉及事务的提交,执行的越多越占用时间,为此可以进行手动事务提交,

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;

主键顺序插入

一般而言主键顺序插入还是乱序插入都可以,建议主键顺序插入

乱序:2 5 22 86 34 8987 213

顺序:1 2 3 4 5 6 7 8 9

插入大量数据

一次性插入大量数据,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';

插入1000000的数据需要16s,如果是使用insert大概需要十几分钟。

·主键优化

数据组织方式:

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

为什么都是根据主键顺序存放的呢:

数据库的数据是根据表空间,段,区,页,行的形式存放的,行就是每一条数据,表是InnoDB管理的最小单位。数据的索引分为聚集索引和二级索引,聚集索引使用的是B+树,它的每个主键都会在树的最下面存放,而前面的向上分裂的部分作为查询数据的索引。所以表的数据都是根据主键顺序组织存放的。

页分裂:

页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。

主键顺序插入的方式:

就从前往后插数据,第一个页写满了,就写入第二个页,然后用一个双向指针维护两个页的关系。

主键乱序插入:

如果是这种情况,第一个页写满了,但是要插入一个id为50的数据,这个时候怎么办呢?

他会新建一个数据页,然后把第一个页分一半出来,把50插入到新的数据页中

然后把链表指针改变一下,把1号的下一个改为3号,3号的next改为2号。

所以:顺序插入的效率最高。

页合并:

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

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

主键设计原则:

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

因为二级索引的最下面存放都是主键,主键太长,就会占用大量的IO和磁盘。

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

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

两个原因:无序且过长

4、业务操作时,避免对主键的修改。

·order by优化

在mysql中有两种排序方式

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

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

所以优化的就需要让排序方式尽量变成Using index。

#没有创建索引时,根据age, phone进行排序
explain select id,age,phone from tb_user order by age , phone;
#创建索引
create index idx_user_age_phone_aa on tb_user(age,phgne);
#创建索引后,根据age, phone进行升序排序
explain select id,age,phone from tb_user order by age , phone;
#创建索引后,根据age, phone进行降序排序
explain select id,age,phone from tb_user order by age desc , phone desc ;

创建了联合

·group by 优化

分组操作的研究中,我们主要研究索引对分组操作的影响。

这是没有索引的查询效率:

下面

是创建索引后的性能:

建立适当的索引,满足最左前缀法则即可提高分组效率。

如果查询时是这样,pro在where,而age在group by也是满足最左前缀法则的。

·limit优化

什么时候要考虑limit进行优化呢:

select * from sb_tu limit 0,10;

第0条数据开始,查10个数据

select * from sb_tu limit 100000,10;

从第100000个数据开始查10个数据。

越往后查询的代价越大,他会把0到100000个数据全部排序,然后丢弃,只返回100000到100010这10个数据。

优化方式:

覆盖索引加子查询

1:先查出想要的id

select id from tb_sku order by id limit 100000,10;

2:表联查查数据

select s.* from tb_sku s , (select id from tb_sku order by id limit 100000,10) a where s.id = a.id;

原方法耗时19s,优化后耗时11.47s。

·count优化

select count(*) from tb_sku;

MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高。如果有where条件就需要一行一行的读取了。

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

目前由于存储引擎的缘故,没有太好的优化count的方法,常用的就是使用像redis之类的数据库进行自我计数。

count的用法:

count(*),count(主键),count(字段),count(1);

count(主键)统计有多少个主键。一般为数据库表的总数。

count(字段)是查询该字段不为空的总数,如果为空则不计入。count(*)同理。

count(1)是给每个字段添加一个1的标识,然后统计1的数量。

几种用法的优劣:

count(主键)

InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)。

count(字段)

没有not null约束: InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null ,不为null,计数累加。有not null约束: InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。

count ( 1 )

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

count (* )

InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。

按照效率排序的话,count(字段)<count(主键id)<count(1) = count(*),所以尽量使用count(*)。

·update优化

我们在更新数据时一定要根据id进行更新,因为在事务中使用id索引进行update用的是行级锁,该行在事务未commit之前不会再被其他人修改。

如果使用字段进行字段进行查询则会被开启表级锁,整张表在该事务未提交之前不会再被修改,会影响其他人操作。

如果字段有建立索引表则不会被添加表级锁,而是行级锁。


http://www.ppmy.cn/devtools/38731.html

相关文章

docker-compose安装 人大金仓数据库

下载官网安装包 将安装包重命名为: kingbase.tar 再导入镜像仓库 docker load -i kingbase.tar目录创建data文件夹创建docker-compose文件 version: 3 services: kingbase: image: kingbase:v1 container_name: kingbaseports: - "54321:54321" volumes: -…

探讨关于AutoPSA里CII算法的结构荷载

UKP3D,AutoPDMS导出应力计算文件至管道应力分析软件分析&#xff0c;如下图AutoPSA.用户咨询如图 1.如果计算时考虑水重&#xff0c;把工况中的w改为ww&#xff1b; 2.CAD表格中结构荷载不是单纯的1.5倍&#xff0c;是参照仿GLIF的算法&#xff0c;计算了水重的&#xff08;根…

设计模式之服务定位器模式

想象一下&#xff0c;你的Java应用是一座庞大的迷宫&#xff0c;里面藏着无数宝贵的服务宝藏&#xff0c;而你正需要一张精确的藏宝图来指引方向&#xff0c;迅速找到并利用这些宝藏。服务定位器模式&#xff0c;正是这样一张神奇的地图&#xff0c;它帮你动态定位并获取应用中…

5.9gunplot绘图堆叠柱状图

gunplot绘图堆叠柱状图 plot"要用的数据&#xff08;后缀名是.dat)" using 2 t(或者title) 跟着是要命名的属性名称 这个名称可以用.dat里的每列列名&#xff0c;也可以直接在后面跟着定义 plot "data.dat" using 2 t columnheader(2), using 3 t column…

Selenium 自动化 —— 常用的定位器(Locator)

什么是定位器 定位器&#xff08;Locator&#xff09;是识别DOM中一个或多个特定元素的方法。 也可以叫选择器 Selenium 通过By类&#xff0c;提供了常见的定位器。具体语法如下&#xff1a; By.xxx("");我们选择单个元素时可以使用findByElement&#xff1a; Web…

JS判断空格符分割失败问题解决方案

JS判断空格符分割失败问题解决方案 JS判断字符串中是否有空格符一般采用**indexOf(" “)**来进行判断&#xff0c;但是这样判断只适用于对普通的空格字符串进行判断&#xff0c;对于特殊的空格字符串会判断失败 **str.indexOf&#xff08;” “&#xff09;**是判断str字…

C++中的构造函数以及默认拷贝构造函数

构造函数 构造函数是在创建类的新对象时自动调用的函数&#xff0c;用于初始化对象的状态。构造函数的名称与类名相同&#xff0c;没有返回类型。 基本构造函数类型&#xff1a; 默认构造函数&#xff1a; 定义&#xff1a;没有参数的构造函数&#xff0c;或者是所有参数都有…

Python中对象数据的持久化操作

含义&#xff1a; 对象数据的持久化操作指的是将Python程序中的对象保存到某种形式的持久化存储介质&#xff08;如文件、数据库&#xff09;中&#xff0c;以便在程序重新运行或在其他程序中使用时能够重新加载这些对象。持久化操作可以确保数据在程序关闭后不会丢失&#xff…