秋招Day14 - MySQL - SQL优化

发布于:2025-06-23 ⋅ 阅读:(23) ⋅ 点赞:(0)

什么是慢SQL?

MySQL中有一个叫做long_query_time的参数,如果SQL的执行时间超过这个参数,就是慢SQL,会被记录到慢查询日志中。

如何优化慢SQL?

通过慢查询日志找到慢SQL,使用EXPLAN查看慢SQL的执行计划,看看有没有索引,大部分情况下都是因为没有索引导致慢SQL。或者可以优化查询条件、减少返回字段等方式进行优化。

慢SQL日志怎么开启?

编辑mysql配置文件my.inf,将slow_query_log设置为1,也可以通过SET GLOBAL命令动态设置

你知道哪些方法优化SQL?

尽可能的少扫描、尽快地返回结果

加索引,比如覆盖索引、让联合索引遵循左前缀原则。

如何利用覆盖索引?   

覆盖索引就是查询所需的字段都在同一个索引里 ,这样mysql就不需要回表,直接从索引中返回结果。

在实际使用中,我会优先考虑把SELECT涉及的字段和WHERE涉及的查询条件一起建立联合索引,并通过EXPLAIN检查是否USING INDEX,确认命中索引

如何正确使用联合索引?

要遵循左前缀原则,也就是说查询条件需要包含索引最左侧字段。

如何进行分页优化?

分页优化是为了避免偏移量过大带来的全表扫描,可以通过延迟关联添加书签来解决。

延迟关联是先通过索引快速定位到所需要的主键id,然后再通过主键id关联其他表获取所需要的完整行数据,减少初始数据加载量并避免回表

原SQL:

SELECT e.id, e.name, d.details
FROM employees e
JOIN department d ON e.department_id = d.id
ORDER BY e.id
LIMIT 1000, 20;

优化后,第一步只查主键,速度快,第二步只处理 20 条数据,效率高:

SELECT e.id, e.name, d.details
FROM (
    SELECT id
    FROM employees
    ORDER BY id
    LIMIT 1000, 20
) AS sub
JOIN employees e ON sub.id = e.id
JOIN department d ON e.department_id = d.id;

添加书签的方式是记住上一次查询返回的最后一行主键值,下一次查询的时候直接从这个主键值开始,不再使用OFFSET

SELECT id, name
FROM users
WHERE id > last_max_id  -- 假设last_max_id是上一页最后一行的ID
ORDER BY id
LIMIT 20;

分页查询为什么会变慢?

因为每次查询都要从第一条数据开始,随着OFFSET的主键增大,要扫描并跳过的数据越来越多

JOIN代替子查询有什么好处?

JOIN的ON条件能够更直接地触发索引,而子查询可能会因为嵌套查询而导致索引失效

JOIN的一次性连接操作替代了子查询的多次重复执行,在数据量大的情况下性能差距明显

JOIN操作为什么要小表驱动大表?

第一,如果大表有索引,小表的每一行都可以通过索引快速匹配大表的相应行。

第二,如果大表没有索引,需要将小表的数据加载到内存,然后全表扫描大表做匹配,这样做比用大表做驱动时时间复杂度更小

为什么要避免用JOIN关联过多的表?

第一,多表JOIN的执行路径会随着表的数量呈现指数级增长,优化器需要估算所有路径的成本

第二,多表JOIN需要缓存中间结果集,中间结果集占用内存过多的话可能会从内存转移到磁盘,性能急剧下降

如何进行排序优化?

  • ORDER BY涉及的字段添加索引,避免file sort,使用联合索引时需与ORDER BY 顺序一致
  • 可以适当修改排序参数,比如增大sort_buffer_size, max_length_for_sort_data等,让排序在内存中完成
  • 可以使用WHERELIMIT来限制需要排序的数据量,减少排序开销。

什么是filesort?

不能利用索引的天然有序性生成排序结果的时候,MySQL需要自己进行排序,如果数据量比较小,会在内存中进行,但是如果数据量比较大就需要写临时文件到磁盘再排序,这叫filesort

全字段排序和rowid排序了解多少?

无法使用索引排序时,MySQL需要在内存中或者磁盘中进行操作,分为全字段排序和rowid排序

  • 全字段排序会一次性取出满足条件行的记录的所有SELECT字段,然后在 sort buffer 中进行排序,排序后直接返回结果,无需回表。优点是只需要进行一次I/O,缺点是内存占用过大,可能会导致filesort
  • rowid排序是只取出主键id和排序字段,存入sort buffer中进行排序,根据排序后的主键id回表取出其他需要的字段。优点是内存占用小,缺点是I/O需要两次。

SELECT + ORDER BY字段的最大长度 <= max_length_for_sort_data, MySQL采用全字段排序避免回表;反之,采用rowid排序避免内存占用过大导致的filesort

Sort_merge_passes参数了解吗?

排序的数据量超过sort_buffer_size时,排序无法完全在内存中进行,MySQL就会使用临时文件进行外部排序,这个时候就会产生Sort_merge_passes这个状态变量,如果这个状态变量短时间内快速激增,代表排序操作的数据量过大,这时需要调整sort_buffer_size的大小。

MySQL执行排序时有两个阶段:

一、内存排序阶段:首先尝试在sort_buffer中进行快速排序。

二、外部排序阶段:如果需要排序的数据量超过sort_buffer_size,MySQL会将数据分割为多个块,每块单独在内存中排序后写入临时文件,然后对这些已排序的块进行归并排序。每次归并操作都会增加Sort_merge_passes的值

条件下推你了解多少?

条件下推是将外层的过滤条件,比如WHERE,JOIN等,尽可能的下推到查询计划的更底层,比如说子查询,连接操作之前,以减少中间结果的数据量,避免外部过滤。

为什么要尽量避免使用SELECT *?

加载冗余数据会占用更多的缓存空间,从而挤占其他重要数据的缓存资源,降低整体系统的吞吐量。

也会增加网络传输的开销,尤其是在大字段的情况下。

最重要的是,SELECT * 可能会导致覆盖索引失效,本来可以直接走索引的查询可能会需要回表

你还知道哪些SQL优化的方法?

避免使用!=或<>操作符

!=和<>操作符是等价的,应该避免使用,因为这会导致MySQL无法使用索引,从而导致全表扫描

可以改成使用column>'aaa' or column<'aaa'

使用前缀索引

比如邮箱从@开始的后几位都是固定的,就可以只对前缀建立索引

alter table test add index index2(email(6));

前缀索引不能用于GROUP BY和ORDER BY

避免在where中对列使用函数 

会导致索引失效,因为MySQL会对每列应用函数之后在进行比较

EXPLAIN平常有用过吗? 

经常用,explain 是 MySQL 提供的一个用于查看 SQL 执行计划的工具,可以帮助我们分析查询语句的性能问题。

一共有十多个输出参数:

比如说type = ALL,key = NULL,就代表全表扫描,这时应该对WHERE语句中的列加索引。

extra = using filesort代表不能使用索引完成排序,可考虑对ORDER BY的字段加索引

常见字段含义了解吗?

type、key、row和extra。

通过它们判断SQL有没有走索引、是否全表扫描、预估扫描行数是否过大、是否触发了filesort或临时表

type的执行效率等级达到什么级别比较合适?

由高到底是system(表只有一行,系统表)、const、eq_ref、ref、range、index(扫描索引的所有叶子节点)和ALL(全表扫描)

一般情况下建议达到const(唯一索引,返回一行结果),eq_ref(多表连接中,通过索引,对每个前表的行,后表最多匹配一行)或ref(非唯一索引),涉及到范围查询到话range也可以。


网站公告

今日签到

点亮在社区的每一天
去签到