MySQL--》快速提高查询效率:SQL语句优化技巧与实践

目录

插入数据

[order by与group by优化](#order by与group by优化)

limit、count、update优化


插入数据

在对数据库当中进行插入数据操作,通常我们都会使用insert进行插入数据,可由于每次insert都会和数据库建立连接,频繁的插入数据就会导致效率上的降低,这里我们就需要对insert插入数据进行一定程度上的优化:

insert优化:对insert优化主要可以从以下几个方面入手

sql 复制代码
-- 批量插入
insert into tb_test values (1, 'Tom'), (2, 'Jerry'), (3, 'Bob');

-- 手动提交事务
start transaction 
insert into tb_test values (1, 'Tom'), (2, 'Jerry'), (3, 'Bob');
insert into tb_test values (4, 'Tom4'), (5, 'Jerry5'), (6, 'Bob6');
insert into tb_test values (7, 'Tom7'), (8, 'Jerry8'), (9, 'Bob9');
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

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

页分裂:当我们插入数据的时候,页会进行分裂,页可以为空页可以填充一半也可以填充100%,每一页包含了2-N行数据(如果一行数据过大,行会溢出),根据主键排列,主键顺序拆入如下所示:

如果我们想在下面的数据中再插入50的数据,由于前两页数据已经满员,因此会开辟一个新的分页

然后会找到第一个数据页50%的位置,23和47是超出了一半了,此时会先将这两个数据移动到新开辟的数据页,然后将新数据50再插入到这个数据页,此时再对链表指针进行一个重新的排布,让1号的数据页的下一个数据页为3号,3号后一个数据页才是2号,这种重新设置排序位置称为"页分裂",所以乱序插入的情况下,可能就会发生页分裂现象:

页合并:当删除一行记录时,实际上记录并没有被物理删除只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用,当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用:

2号数据页被删除一半数据之后,会检索后面3号数据页是否有合并的可能性,如果有就合并到2号数据页,那么3号数据页就可以空闲出来了:

空闲出来的数据页,如果后面再插入数据的话就可以直接插入到3号数据页当中就可以了:

主键设计原则

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

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

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

4)业务操作时,避免对主键的修改。
load:如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可使用MySQL数据库提供的load指令进行插入,可以将复合一定规律的文件通过load操作变成表数据:

使用load加载数据的主要步骤如下所示(这里主键顺序插入的性能也是高于乱序插入):

sql 复制代码
-- 客户端连接服务器时,加上参数 --local-infile
mysql --local-infile -u root -p

-- 查看是否开启本地文件导入开关
select @@local_infile;

-- 设置全局参数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';

order by与group by优化

order by优化:对order by进行优化主要分为以下两种情况:

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

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

sql 复制代码
-- 没有创建索引时,根据age,phone进行排序
explain select id, age, phone from emp order by age, phone;

-- 创建索引
create index idx_emp_age_phone on emp(age, phone);

-- 创建索引后,根据age,phone进行升序排序
explain select id, age, phone from emp order by age, phone;

-- 创建索引后,根据age,phone进行降序排序
explain select id, age, phone from emp order by age desc, phone desc;

-- 创建索引
create index idx_emp_age_phone_ad on emp(age asc , phone desc);

-- 创建索引后,根据age,phone进行降序排序
explain select id, age, phone from emp order by age asc, phone desc;

order by使用注意

1)根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则

2)尽量使用覆盖索引

3)多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)

4)如果不可避免的出现filesort大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)

group by优化:在进行分组操作时,也可以通过索引来提高效率,当然索引的使用也是需要满足最左前缀法则,如下所示:

limit、count、update优化

limit优化:一个常见又非常头疼的问题就是limit2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录其他记录丢弃查询排序的代价非常大,优化思路:一般分页查询时通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化:

sql 复制代码
explain select * from tb_sku t , (select id from tb_sku order by id limit 2000000,10) a wheret.id = a.id;

count优化:MISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数效率很高;InnoDB引擎就麻烦了,它执行count(*)的时候需要把数据一行一行地从引l擎里面读出来然后累积计数,优化思路:一般是通过自己定义的key和value自己进行计算,新增就累加删除就累减少,自己计数的方式进行:

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

count用法主要有以下几种

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

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

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

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

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

update优化:InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁:

sql 复制代码
update student set no='2000100100'where id = 1;
update student set no='2000100105' where name='韦一笑';
相关推荐
Zzz 小生2 小时前
Claude Code学习笔记(四)-助你快速搭建首个Python项目
大数据·数据库·elasticsearch
nongcunqq5 小时前
abap 操作 excel
java·数据库·excel
rain bye bye6 小时前
calibre LVS 跑不起来 就将setup 的LVS Option connect下的 connect all nets by name 打开。
服务器·数据库·lvs
冻咸鱼6 小时前
MySQL的配置
mysql·配置
我不是QI7 小时前
DES 加密算法:核心组件、加解密流程与安全特性
经验分享·算法·安全·网络安全·密码学
阿里云大数据AI技术7 小时前
云栖实录|MaxCompute全新升级:AI时代的原生数据仓库
大数据·数据库·云原生
不剪发的Tony老师8 小时前
Valentina Studio:一款跨平台的数据库管理工具
数据库·sql
重生之我要当java大帝8 小时前
java微服务-尚医通-编写医院设置接口下
java·开发语言·sql
weixin_307779138 小时前
在 Microsoft Azure 上部署 ClickHouse 数据仓库:托管服务与自行部署的全面指南
开发语言·数据库·数据仓库·云计算·azure
六元七角八分8 小时前
pom.xml
xml·数据库