SQL的性能分析、优化

SQL性能分析

SQL执行频率

MySQL 客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:

-- session 是查看当前会话 ;
-- global 是查询全局数据 ;
SHOW GLOBAL STATUS LIKE 'Com_______';

在这里插入图片描述Com_delete: 删除次数
Com_insert: 插入次数
Com_select: 查询次数
Com_update: 更新次数
通过上述指令,可以查看到当前数据库到底是以查询为主,还是以增删改为主,从而为数据库优化提供参考依据。 如果是以增删改为主,可以考虑不对其进行索引的优化。 如果是以查询为主,那么就要考虑对数据库的索引进行优化了。

慢查询日志–查看耗时

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。
MySQL的慢查询日志默认没有开启,可以首先查看一下系统变量 slow_query_log。
在这里插入图片描述如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

# 开启MySQL慢日志查询开关
slow_query_log=1
# 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2

配置完毕之后,通过以下指令重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息
/var/lib/mysql/localhost-slow.log。

systemctl restart mysqld 

在这里插入图片描述

profile详情

show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持profile操作:

SELECT @@have_profiling ; 

在这里插入图片描述可以看到,当前MySQL是支持 profile操作的,但是开关是关闭的。可以通过set语句在
session/global级别开启profiling:

SET profiling = 1; 
#首先执行一系列查询语句
select * from tb_user;
select * from tb_user where id = 1;
select * from tb_user where name = '白起';
select count(*) from tb_sku;

执行一系列的业务SQL的操作,通过如下指令查看指令的执行耗时:

-- 查看每一条SQL的耗时基本情况
show profiles;
-- 查看指定query_id的SQL语句各个阶段的耗时情况
show profile for query query_id;
-- 查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;
#直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;

explain

EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
在这里插入图片描述Explain 执行计划中各个字段的含义:

在这里插入图片描述

SQL语句优化

插入数据Insert时的优化

当往表中插入多条数据时:

方案一:批量插入数据

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;

方案三:主键顺序插入,性能要高于乱序插入。(将表的主键设置为自增)

大批量插入数据

如果一次性需要插入大批量数据(比如: 几百万的记录),使用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' ;
#加载本地/root/sql1.log中的数据到tb_user表中,各个字段通过,分割,行之间通过换行分割

主键优化

在InnoDB引擎中,数据行是记录在逻辑结构 page 页中的,而每一个页的大小是固定的,默认16K。那也就意味着, 一个页中所存储的行也是有限的,如果插入的数据行row在该页存储不下,将会存储到下一个页中,页与页之间会通过指针连接。如果按照主键顺序插入,当存储空间不够,直接申请新的一页,页与页之间也会通过指针连接,单数主键如果乱序插入,会产生页分裂的现象,降低性能。

  • 设计原则:
    1、满足业务需求的情况下,尽量降低主键的长度。
    2、插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
    3、尽量不要使用UUID做主键或者是其他自然主键。
    4、业务操作时,避免对主键的修改

order by优化

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

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, 排序性能较低。

在age,phone上创建索联合索引引之后排序

-- 创建索引
create index idx_user_age_phone_aa on tb_user(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 index,使用age排序的语句遵循了最左匹配原则,所以联合索引依旧有效。

#创建索引后,根据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。

#根据phone,age进行升序排序,phone前,age在后。  
explain select id,age,phone from tb_user order by phone , age; 

在这里插入图片描述排序时,也需要满足最左前缀法则,否则也会出现 filesort。因为在创建索引的时候, age是第一个字段,phone是第二个字段,所以排序时,也就该按照这个顺序来,否则就会出现 Using filesort。

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

在这里插入图片描述因为创建索引时,如果未指定顺序,默认都是按照升序排序的,而查询时,一个升序,一个降序,此时就会出现Using filesort。

order by优化原则:

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

group by优化

#在没有索引的情况下,执行如下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); 
explain select profession , count(*) from tb_user group by profession ; 

在这里插入图片描述在这里插入图片描述在这里插入图片描述如果仅仅根据age分组,就会出现 Using temporary ;而如果是 根据 profession,age两个字段同时分组,则不会出现 Using temporary。原因是因为对于分组操作,在联合索引中,也是符合最左前缀法则的。

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

limit优化

在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低。
优化思路:一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

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

count优化

count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是NULL,累计值就加 1,否则不加,最后返回累计值。
用法:count(*)、count(主键)、count(字段)、count(数字)
在这里插入图片描述按照效率排序的话,count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽
量使用 count(*)。

update优化

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁 ,并且该索引不能失效,否则会从行锁
升级为表锁 。也就是说通过where 主键或者索引更新,则是行级锁,通过其他字段更新则转为表级锁。

学习视频网址:【黑马程序员 MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化全囊括-哔哩哔哩】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值