Featured image of post 计算机基础 数据库 SQL优化专题

计算机基础 数据库 SQL优化专题

🌏SQL 🎯 这篇文章用于记录有关【数据库 SQL优化】的学习记录

概述

所谓的SQL优化其实大多数时候都是指对查询SQL的优化,因为查询SQL在应用中SQL占比为绝大部分

INSERT SQL优化

非大批量数据

(1) 批量插入 而非逐个插入
(2) 手动提交事务 而非自动提交事务
(3) 主键顺序插入 而非乱序插入

大批量数据(例如100万)

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

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

#设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
select @@local-infile; #查询导入数据的开关是否开启
set global local-infile = 1; #开启从本地加载文件导入数据的开关

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

使用load指令插入100万条数据的时间为16.83s,而使用insert语句进行插入大概需要10min,可见load指令对于插入大批量的数据效率很高。

主键优化

InnoDB的数据组织方式

在InnDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表被称为索引组织表(index organized table - IOT)

Tablespace - Segment - Extent(1M 64x16KB) - Page(16KB) - Row(大小?)

页分裂

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

主键设计原则

(1) 满足业务要求的情况下,尽量降低主键的长度
(2) 插入数据的时候,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键
(3) 尽量不要使用UUID做主键或者是其他自然主键,如身份证
(4) 业务操作时,避免对主键的修改

GROUP BY 分组优化

(1)在分组的操作时,可以通过索引来提高效率

(2)分组操作时,索引的使用也是满足最左前缀法则的

explain select profession, count(*) from tb_user group by profession;
#创建索引
create index idx_user_pro_age_sta on tb_user(profession, age, status);
#执行分组操作,根据profession字段分组
explain select profession count(*) from tb_user group by profession;
explain select profession count(*) from tb_user group by profession, age;

ORDER BY 排序优化

(1) Using fileSort: 通过表的索引或者全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫做FileSort排序
(2) Using index: 通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高

创建表

create table user (
	id int AUTO_INCREMENT primary key,
    name varchar(32),
    age int,
    phone varchar(11)
);

插入测试数据

insert into user (name, age, phone) values ('zhangsan', 21, '15611112222'),('lisi', 19, '13311112222'),('wangwu', 23, '17811112222'),('zhaoliu', 20, '15611116666');

优化历程1

#无索引 -> Using filesort
explain select id,age,phone from user order by age,phone;

#创建索引
create index idx_age_phone_aa on user(age, phone);

#有索引 再次查询
explain select id,age,phone from user order by age,phone;

explain select id,age,phone from user order by age DESC, phone DESC;

explain select id,age,phone from user order by age DESC, phone ASC;

优化历程2 (Using index; Using filesort)

针对上方的 (Using index; Using filesort) 再次进行优化,可以做的优化操作

create index idx_age_phone_da on user(age DESC, phone ASC);

LIMIT 优化

limit 2000000,10的OFFSET很大的时候,查询的效率会很慢,优化的思路一般是通过创建覆盖索引 + 子查询

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

COUNT 优化

count(主键)

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

count(其他字段)

(1)如果没有not null的约束,InnDB会遍历整张表把每一行的字段都取出来,返回给服务层,服务层判断是否为null,不为null,则直接按照行进行累加;

(2)如果有not null的约束,InnDB引擎会遍历整张表把每一行的字段都取出来,返回给服务器,直接按照行进行累加

count(1)

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

count(*)

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

count效率总结

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

Licensed under CC BY-NC-SA 4.0