MySQL学习笔记-3.如何提高sql语句的查询性能

文章目录

总览

  • 优化慢查询

1. 慢查询的怀疑方向

在这里插入图片描述

2. 覆盖索引

在这里插入图片描述

3. 索引基数

在这里插入图片描述

4. count()函数

在这里插入图片描述

5. ORDER BY

在这里插入图片描述

6. RAND()函数

在这里插入图片描述

7. 索引下推

在这里插入图片描述

8. 松散索引扫描

在这里插入图片描述

9. 字段函数操作

在这里插入图片描述

  • 原则是不动索引字段,去动别的字段

10. 分页查询

在这里插入图片描述

1. Sakila-db的安装使用

1.1 Sakila-db的认识

在这里插入图片描述

1.2 下载与安装

官网下载地址

在这里插入图片描述
在这里插入图片描述

wget命令下载

在这里插入图片描述

tar -xvf解压

在这里插入图片描述

执行sakila-schema.sql建表语句和导入data数据
source /root/sakila-db/sakila-schema.sql
source /root/sakila-db/sakila-data.sql 
用MySQL Workbench 打开sakila.mwb文件

在这里插入图片描述

2. where 查询太慢怎么办

2.1 覆盖索引

在这里插入图片描述
在这里插入图片描述

  • 因此,如果不是业务需要,尽量不适用select *
  • 增加联合索引,也有一定代价,即磁盘空间的占用变大
    在这里插入图片描述
  • 复制数据表,剔除联合索引,进行性能比较
1. 查看inventory建表语句
show create table inventory;

在这里插入图片描述

2. 复制建表语句-剔除联合索引和外键
  • 复制建表语句
CREATE TABLE `inventory1` (
  `inventory_id` mediumint unsigned NOT NULL AUTO_INCREMENT,
  `film_id` smallint unsigned NOT NULL,
  `store_id` tinyint unsigned NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`inventory_id`),
  KEY `idx_fk_film_id` (`film_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4582 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  • 将之前的表的数据到新的表里
insert into inventory_1 select * from inventory;
3. 查询语句-进行测试

在这里插入图片描述

  • explain查看执行计划
    在这里插入图片描述
  • 对于删除联合索引的数据表inventory_1没有走任何索引,进行了全局扫描
    在这里插入图片描述
  • 查询原表,走了index索引查询

3. MySQL使用索引规则以及如何重新走正确的索引

在这里插入图片描述

3.1 MySQL选取索引规则

测试:
  • 创建一个单字段表,将原表数据导入5次
    在这里插入图片描述
  • 将新表的数据打乱
update city_1 set city = 
(select city from city order by rand() limit 1);
  • 增加前缀索引
    在这里插入图片描述
    在这里插入图片描述
  • 会根据区分度去使用索引,区分度越大,且使用空间不大的索引

3.2 强制使用索引

在这里插入图片描述

3.3 优化索引

在这里插入图片描述

4. 优化COUNT查询语句

在这里插入图片描述

4.1 count()函数认识

在这里插入图片描述

4.2 测试count函数性能差异

在这里插入图片描述

1. 使用非索引字段

在这里插入图片描述

  • 走了全表扫描
  • 将全表扫描所有行,储存引擎将行中的指定字段first_name解析出来,交给Server层,进行逐个判空处理
    在这里插入图片描述
2. 使用普通索引字段和主键索引字段

在这里插入图片描述

  • 可以使用覆盖索引,因此只需要查询辅助索引,无需回表,也无需解析字段
  • 但是辅助索引可以为null, 故Server层还是需要每次判断字段是否为null
  • 即使使用主键索引,MySQL没有对主键索引优化,储存引擎还是会将所有含有主键的索引交给Server层,Server层还是会逐一判断字段是否为null
    在这里插入图片描述
count(1)

在这里插入图片描述
在这里插入图片描述

3. count(*)
  • MySQl真正对count(*)做了优化
  • 直接在B+树中,查询到数据个数,然后直接返回,无需提交给server层进行逐个字段判断处理,因此,理论上效率是最高的

在这里插入图片描述

5. 优化ORDER BY 语句

在这里插入图片描述

  • 如果能够索引覆盖,效率是最高的
  • 不能索引覆盖,从两个角度考虑调优
    • 1是中间表的阀值,根据需要,如果调大一些,多走内存
    • 2是中间表行的字段,根据需要,如果调大一些,就不会先排序两个字段,再回表填充数据

5.1 order by 步骤原理

在这里插入图片描述

5.2 优化策略

1. 条件查询

在这里插入图片描述

2. 中间结果集处理

在这里插入图片描述

  • 因此,若内存空间足够,可以调节sort_buffer_size中间表在内存中的阀值
3. 回表生成完整结果集

在这里插入图片描述

  • 当大于一个阀值时,MySQL会优化中间表,将只生成两个字段的中间表,但是需要回表填充最终结果
  • 阀值过大,中间表过大,效率不一定最好,因为,1是会大于内存中的阀值,会落盘到硬盘,效率降低。2是数量过多,排序性能受影响。
    在这里插入图片描述
4. 最高效-索引覆盖

在这里插入图片描述
在这里插入图片描述

  • 即筛选字段、排序字段、输出字段都在一个联合索引中,可以使用索引覆盖,跳过中间表,直接输出最终筛选和排序的结果

6. random随机函数作用及性能优化

6.1 RAND()原理

  • 该函数生成0~1随机的数
    在这里插入图片描述
1. 创建第一个临时表

在这里插入图片描述

2. 将原表行记录转为临时表的所有行记录

在这里插入图片描述

  • 最终将原表的所有行数据-转为临时表的所有行记录
3. 针对临时表,创建sort_buffer第二张临时表

在这里插入图片描述

  • 临时表若在内存里,找行位置字段,如果在硬盘里,找临时表的隐藏主键
4. 对sort_buffer排序,取出第一个行位置,查询临时表

在这里插入图片描述

6.2 为什么会慢

在这里插入图片描述

6.3 优化策略

1. 临时方案

在这里插入图片描述

  • 也有可能找不到,概率比较小
  • sql语句比较复杂,真实业务中,排除和修补bug会有麻烦
2. 业务方案

在这里插入图片描述

7. 带头大哥丢了,怎么办

  • 即联合索引的左侧原则,不能走索引,只能走全局扫描的两种特殊场景的优化
    在这里插入图片描述

7.1 索引下推


在这里插入图片描述

  • 不能走联合索引,主要排序字段,只是一个范围,第二个字段是无序的,需要回表,如上例,需要回四次表
  • MySQl5.6以后优化,直接在索引中查找,无需扫描多行记录

在这里插入图片描述

7.2 松散索引扫描

在这里插入图片描述
在这里插入图片描述

  • 首先扫描联合索引的每个带头大哥,比如store_id,然后第二个索引就有序了,再扫描查询第二个索引film_id无需遍历每个索引,当索引值大于要查询的字段时,直接跳过,因此,会减少很多次查询

8. 明明有索引,就是不走,怎么办

在这里插入图片描述

8.1 问题产生

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 因为字段加了函数后,破坏了字段原有的顺序,因此,不能再使用字段的索引,而是找其他索引,如果找不到,就走全局扫描

8.3 不走字段索引的其他例子

1. 字符串与数字转换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 隐式字符编码转换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9. 分页查询优化

在这里插入图片描述

  • 即首先尝试能否是否排序字段、查询字段的联合索引,进行索引覆盖
  • 如果不能使用索引覆盖,考虑使用排序字段索引查询指定的数据id,再用指定数据与原表联表查询,回表查询到指定id的其他字段

9.1 问题产生

在这里插入图片描述

  • 先排序,再丢弃,性能浪费严重

9.2 优化策略

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值