什么是慢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:
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 条数据,效率高:
sql
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
sql
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等,让排序在内存中完成
- 可以使用WHERE和LIMIT来限制需要排序的数据量,减少排序开销。
什么是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'
使用前缀索引
比如邮箱从@开始的后几位都是固定的,就可以只对前缀建立索引
sql
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也可以。