【MySQL】SQL 优化

MySQL - SQL 优化

1. 在 MySQL 中,如何定位慢查询?

1.1 发现慢查询

现象:页面加载过慢、接口压力测试响应时间过长(超过 1s)

可能出现慢查询的场景:

  • 聚合查询
  • 多表查询
  • 表数据过大查询
  • 深度分页查询

1.2 通过现象定位到问题是出在 MySQL 的慢查询(查看慢日志法)

找到配置文件:

Windows:my.ini

在这里插入图片描述

Linux:/etc/my.cnf

在这里插入图片描述

添加或修改两个属性:

  1. slow_query_log=1(1 为 true,开启慢日志)
  2. long_query_time=2(单位为秒,超过 2 秒的将记录在慢日志)

选择性添加或修改,slow_query_log_file 属性,慢日志名

  • Linux 在/var/lib/mysql/localhost-slow.log
  • Windows 见配置文件默认值

重启 MySQL:

  • Windows

    • net stop MySQL
      
    • net start MySQL
      
  • Linux

    • sudo systemctl stop mysql
      
    • sudo systemctl start mysql
      

发现问题,找到对应的慢日志,定位到问题是出在 MySQL 的慢查询:

在这里插入图片描述

1.3 回答问题

  1. 介绍一下当时产生问题的场景(我们当时的一个接口测试的时候非常的慢,压力测试的结果大概 5 秒钟);
  2. 而我们在调试阶段,开启了 MySQL 的慢日志记录,我们设置的值为 2 秒,一旦 sql 执行超过 2 秒就会记录在慢日志中,我们发现问题后查询了 MySQL 的慢日志,最终定位到问题是出在 MySQL 的慢查询;

2. 那这个 SQL 语句执行很慢,是如何分析的呢?

可以采用 MySQL 自带的分析工具 explain

  • 通过 key 和 key_len 查询是否命中索引,也可以判断索引本身存在是否失效的情况;
  • 通过 type 字段查看 sql 是否有进一步的优化空间,是否村咋全索引扫描或者全盘扫描;
  • 通过 Extra 建议判断是否出现了回表的情况,如果出现了,可以尝试添加索引或者修改返回字段来修复;

在这里插入图片描述

  • possible_key 当前 sql 可能会使用到的索引

  • key 当前 sql 实际命中的索引

  • key_len 索引占用的大小

  • Extra 额外的优化建议

    在这里插入图片描述

  • type 这条 sql 的连接类型,性能由好到差:NULL、system、const、eq_ref、ref、range、index、all

    • NULL:没有使用到表
    • system:查询 MySQL 系统内置的表
    • const:根据主键索引查询
    • eq_ref:主键索引查询或者唯一索引查询
    • ref:索引查询
    • range:分为查询
    • index:索引树(全索引)扫描
    • all:全盘扫描

3. 了解过索引吗?(什么是索引)

3.1 索引是什么

索引(index)是帮助 MySQL 高效获取 数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构**(B+ 树)**,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

3.2 什么是 B+ 树

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.3 回答

了解过索引吗?

  • 索引(index)是帮助 MySQL 高效获取数据的有序的数据结构;
  • 提高数据检索的效率,降低数据库的 IO 成本,因为不需要全表扫描;
  • 通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU 的消耗;

索引列:以表中哪个列来创建索引;

索引的底层数据结构了解过吗?

InnoDBMySQL数据库管理系统中的一种事务性存储引擎。

MySQL 的 InnoDB 引擎采用的 B+ 树的数据结构来作为索引的存储结构;

  • MySQL 的索引的底层数据结构是 B+ 树;
  • 阶数更多,路径更短;磁盘读写代价低,非叶子节点只存储指针,叶子节点存储数据;
  • B+ 树便于扫库和区间的查询,叶子节点是一个双向链表;

4. 什么是聚簇索引,什么是非聚簇索引?

4.1 什么是聚集索引,什么是二级索引(非聚集索引)?

在这里插入图片描述

在这里插入图片描述

如果没有主键,则会使用隐藏字段:DB_ROW_ID,隐藏主键

4.2 什么是回表查询?

了解什么是聚集索引,什么是二级索引(非聚集索引)后,再进行理解:

在这里插入图片描述

如果没有主键,则会使用隐藏字段:DB_ROW_ID,隐藏主键进行回表查询(如果不给隐藏主键创建索引,那么回表查询是没有走索引的,效率低下)

4.3 回答

  • 聚簇索引(聚集索引):数据存放到索引中,B+ 树的叶子节点保存了整行数据,有且只有一个;
  • 非聚簇索引(二级索引,非聚集索引):数据不全部存放到索引中,B+ 树的叶子节点保存了索引列以及对应的主键,可以有多个;
  • 通过二级索引找到对应的主键值,再到聚集索引中查找整行数据,这个过程就是回表;(如果没有聚集索引,回表查询就不是通过索引查询了,而是全表查询,非常低性能)

5. 知道什么叫覆盖索引吗?

覆盖索引就是查询使用了索引,并且需要返回的列在该索引中已经全部覆盖到了;

在这里插入图片描述

  • 聚集索引一定是,二级索引不需要回表查询也是;

回答:

  • 聚集索引指的是查询使用了索引,返回的列,在索引中全部都能找到;
    • 使用 id 查询,直接走聚集索引查询,一次索引扫描,直接返回全部数据,性能高;
    • 如果所需列在索引中不存在,就会触发回表查询,所以尽量避免使用 select *

6. MySQL 超大分页怎么处理?

在数据量比较大的时候,如果进行 limit 分页查询,在查询的时候,越往后,分页查询效率越低。

6.1 超大分页场景

例如:

select * from user order by nickname limit 0, 10;
select * from user order by nickname limit 9000000, 10;

上面那个 0 毫秒不到,而下面那个甚至可以达到 10 秒以上!

因为,在执行的时候,需要加载 9000010 条记录(每条都是 raw),再选取 9000000 - 9000010 的记录,其他记录丢弃,查询排序的非常大。

而 nickname 在这里不是覆盖索引,所以加载 9000010 条记录时,性能很低。

  • order by 子句使用索引需要:
    1. order by 子句中的字段必须创建了索引,索引查询的字段覆盖需要查询的字段;
    2. order by 子句中的字段要符合最左前缀法则(对于复合索引);
  • 像这种非覆盖索引,回表的性能还不如全表查询呢,所以不走索引在这里是好事;

6.2 超大分页 SQL优化

但是我们知道这条 sql 中是覆盖索引:

select id from user ordery by nickname limit 9000000, 10;

那么我们再拿这 10 个 id 去表中查询即可。

因此 sql 可以优化成这样(覆盖索引 + 子查询):

select * from
	user u, 
	(select id from user order by nickname limit 9000000, 10) a
where u.id = a.id;

6.3 回答

  • 问题在于在数据量比较大时,limit 分页查询,需要对数据进行排序,效率低。

  • 可以用到索引(有序性)查询,而如果不是覆盖索引,那么可以用覆盖索引 + 子查询进行优化!

7. 索引创建的原则有哪些?

先陈述自己实际开发中怎么用索引的,用了什么索引,如主键索引、唯一索引、复合索引…

再说原则:

  1. 数据量较大,且查询比较频繁的表;(10w+ 就可以创建索引增加用户体验了)
  2. 常常作为查询条件、排序操作、分组操作的字段;
  3. 尽量使用联合索引(多列索引),减少单列索引,这样可以让查询更可能是覆盖索引;
  4. 要控制索引的数量,并不是越多越好,增删改都是需要维护的;
  5. 字段内容区分度高,尽量建立唯一索引,区分度越高性能越好;
  6. 字符串类型字段,内容较长,可以使用前缀索引;
  7. 如果索引列不能存储 NULL 值,在创建表的时候使用 NOT NULL 约束,这有利于让优化器选择哪个索引进行更有效的查询;

8. 什么情况下索引会失效?

8.1 复合索引

在这里插入图片描述

顺序见 Seq_in_index,即 name、status、address

8.2 违反最左前缀法则

SQL 的查询条件 / 排序 / 分组从索引的最左前列开始,才会走索引:

在这里插入图片描述

正向例子:

在这里插入图片描述

反向例子:

在这里插入图片描述

跳过某一列,则只有部分最左前缀索引生效:

在这里插入图片描述

8.3 范围查询右边的列,不能使用索引

在这里插入图片描述

下面那个,name 和 status 走索引,status 右边的字段 address 没用到索引。

8.4 不要再索引列上进行运算操作,索引会失效

在这里插入图片描述

8.5 字符串不加单引号,造成索引失效

在这里插入图片描述

复杂行为往往导致索引失效~

8.6 模糊查询有可能会导致索引失效

头部模糊匹配,索引失效。如果仅仅是尾部模糊匹配,索引不会失效。

在这里插入图片描述

8.7 回答

同理,先陈述自己的遭遇,如某个场景创建了索引,性能还是很慢,explain 去查看 sql 语句的执行计划,发现索引失效了。

对于复合索引:

  1. 违反最左前缀法则;
  2. 范围查询右边的列;
  3. 在索引列上进行运算操作;
  4. 字符串不加单引号,导致 MySQL 优化器进行类型转化;
  5. 头部模糊查询;

通常情况下,可以使用 explain 查看 sql 的执行计划来判断索引是否失效。

9. 谈一谈你对 SQL 优化的经验

从三个方面:

  1. 表的设计优化
  2. 索引优化(参考优化创建原则和索引失效)
  3. SQL 语句优化

9.1 表的设计优化(参考阿里开发手册《嵩山版》)

  1. 比如设置合适的数值(tinyint、int、bigint)要根据实际情况选择;
  2. 比如设置合适的字符串类型(char,varchar)char定长效率高,varchar可变长度,效率稍低;

9.2 SQL 语句的优化

  1. select 语句务必指明字段的名称(避免使用 select *);
  2. SQL 语句要避免造成索引失效的写法;
  3. 尽量用 union all 代替 union,union 会多一次过滤,效率低(union 会将重复的过滤掉,如果知道没有重复的就可以用 union all)

在这里插入图片描述

这个就不能用 union all,并不是存在绝对地去优化,而是看情况决定~

  1. 避免在 where 子句对字段进行表达式/函数操作;

  2. join 优化,能用 inner join 就不用 left join 或者 right join,如果必须使用,一定要以小表为驱动;

    • 小表:数据量较小的表,大表:数据量较大的表,

    • 这样的好处就是以小表连接大表,连接次数较小,on 子句查询的次数较小,并且由于是查询大表,所以如果是有索引,索引效果更明显!

    • 内连接会对两个表进行优化,优先把小表放外边,把大表放里面;

    • left join 或者 right join 则不会重新调整顺序;

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s:103

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值