面试必考:SQL查询优化

你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。

点击下方👇关注公众号,带你一起复习后端技术,看看面试考点,补充积累技术知识,每天都为面试准备积累


文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读


SQL查询优化是提升数据库性能的核心手段之一,特别是当面对大量数据或复杂查询时,优化 SQL 查询能够显著减少数据库的响应时间。以下是几个常见的 SQL 查询优化技巧,并通过实际例子来详细说明每个优化方法。



01
使用合适的索引


索引是数据库查询的加速器。没有索引的查询,尤其是涉及大量数据时,可能会非常慢,因为数据库必须扫描整个表来查找匹配的记录。

例子:

假设有一个 users 表,如下所示:

CREATE TABLE users (    id INT PRIMARY KEY,    name VARCHAR(255),    email VARCHAR(255),    age INT);

如果你经常按照 email 字段查询用户信息,你应该为 email 列创建索引,以加速查询。

-- 为 email 列创建索引CREATE INDEX idx_email ON users(email);

优化前的查询:

SELECT * FROM users WHERE email = 'john.doe@example.com';

没有索引的情况下,数据库必须扫描整个表来查找匹配的记录,速度很慢。通过创建索引后,查询速度会显著提高。

例子:使用复合索引

如果查询经常涉及多个字段,例如按 name 和 age 查询,你可以创建一个复合索引:

-- 创建复合索引,优化多条件查询CREATE INDEX idx_name_age ON users(name, age);


优化前的查询:

SELECT * FROM users WHERE name = 'John' AND age > 30;

有了复合索引后,查询会更高效,因为 MySQL 会使用复合索引来加速查询。


02
避免 SELECT *


SELECT * 会选择所有列,这通常是不必要的,并且可能导致大量不必要的数据被返回。应该明确只选择所需的字段。

例子:

优化前的查询:

SELECT * FROM users WHERE id = 1;

假设你只关心 name 和 email 字段,使用 SELECT * 会返回所有字段,这样不但浪费资源,还会增加网络传输的开销。

优化后的查询:

SELECT name, email FROM users WHERE id = 1;


03
避免不必要的 ORDER BY


ORDER BY 会对查询结果进行排序,可能会导致性能下降,特别是当排序的列没有索引时。应尽量避免不必要的排序操作,或者确保排序的列上有索引。

例子:

优化前的查询:

SELECT * FROM users ORDER BY age DESC;

如果 age 字段上没有索引,ORDER BY 操作将会非常慢,尤其是当数据量很大时。你可以创建索引来加速排序:

-- 为 age 字段创建索引CREATE INDEX idx_age ON users(age);

优化后的查询:

SELECT * FROM users ORDER BY age DESC;

有了索引后,排序操作将会变得更快。


04
避免在 WHERE 子句中使用函数


在 WHERE 子句中使用函数会导致全表扫描,因为索引无法用于函数计算结果。

例子:

优化前的查询:

SELECT * FROM users WHERE YEAR(birthdate) = 1990;

YEAR(birthdate) 这样的查询会导致 birthdate 列上的索引失效,因为函数 YEAR(birthdate) 会先被计算,无法利用索引进行加速。

优化后的查询:

可以改为通过一个范围查询来避免函数的使用:

SELECT * FROM users WHERE birthdate >= '1990-01-01' AND birthdate < '1991-01-01';

这样可以利用 birthdate 上的索引,提高查询效率。


05
避免不必要的 JOIN


在查询中,如果你使用了多个表的连接(JOIN),要确保连接条件的字段上有索引,否则会导致全表扫描,影响性能。如果某个表的数据量非常大,考虑将查询拆解成多个小查询。

例子:

假设有两个表:users 和 orders,关系如下:

CREATE TABLE users (    id INT PRIMARY KEY,    name VARCHAR(255));CREATE TABLE orders (    id INT PRIMARY KEY,    user_id INT,    order_date DATE,    amount DECIMAL);

优化前的查询:

SELECT u.name, o.order_date, o.amountFROM users uJOIN orders o ON u.id = o.user_idWHERE u.name = 'John' AND o.amount > 100;

如果 user_id 在 orders 表中没有索引,连接查询会非常慢,MySQL 需要进行全表扫描。解决办法是确保 orders 表中的 user_id 列有索引。

CREATE INDEX idx_user_id ON orders(user_id);

优化后查询性能会大幅提高。


06
避免使用 IN 子句查询大量数据


IN 子句在处理大量数据时性能较差,尤其是当 IN 中包含大量值时。可以考虑将 IN 子句转化为 JOIN 或 EXISTS 子句来优化查询。

例子:

优化前的查询:

SELECT * FROM users WHERE id IN (12345, ... , 1000);

当 IN 中的值很多时,MySQL 可能会进行全表扫描,查询性能差。

优化后的查询:

-- 使用 JOIN 替代 INSELECT u.* FROM users uJOIN (SELECT id FROM some_other_table WHERE some_condition) t ON u.id = t.id;

或者使用 EXISTS 来代替 IN:

SELECT * FROM users uWHERE EXISTS (SELECT 1 FROM some_other_table t WHERE u.id = t.id AND t.some_condition);


07
避免大数据集的全表扫描


在大数据表上进行查询时,务必使用合适的索引,以避免全表扫描。对于没有索引的查询,可以考虑创建索引来加速查询。

例子:

优化前的查询:

SELECT * FROM large_table WHERE column1 = 'value';

如果 column1 没有索引,查询会非常慢。你可以为 column1 创建索引:

CREATE INDEX idx_column1 ON large_table(column1);

创建索引后,查询将利用索引加速。


08
合理使用 LIMIT


如果查询的结果只需要一部分数据,使用 LIMIT 可以避免返回过多不必要的记录,减少数据库和网络的负担。

例子:

优化前的查询:

SELECT * FROM users ORDER BY created_at DESC;

如果只需要最新的 10 个用户,可以使用 LIMIT 来限制返回的记录数:

SELECT * FROM users ORDER BY created_at DESC LIMIT 10;


09
使用查询缓存


MySQL 支持查询缓存功能,可以缓存常用的查询结果,避免重复执行相同的查询。尽管在现代的 MySQL 版本中(例如 5.7+)查询缓存已被弃用,但在适合的场景下(如查询频繁但数据更新不频繁),仍然可以启用查询缓存来提升性能。

SET GLOBAL query_cache_size = 1000000-- 设置查询缓存大小SET GLOBAL query_cache_type = 1;       -- 启用查询缓存


10
总结



SQL 查询优化涉及很多方面,包括合理使用索引、避免全表扫描、减少不必要的排序和计算、避免不合理的 JOIN、合理拆分查询等。通过对 SQL 查询的分析和调优,可以显著提高 MySQL 数据库的查询性能。通过使用 EXPLAIN 分析查询的执行计划、查看慢查询日志,结合这些优化方法,可以有效减少慢查询,提高系统的响应速度。

今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!

END


扫码关注

一起积累后端知识
不积跬步,无以至千里
不积小流,无以成江海

喜欢此内容的人还喜欢


《Java面试题指南》回归啦~


一个阿里二面面试官必问的问题


Lambda表达式说爱你不容易


分享面试:mysql数据库索引失效的情况


Spring-Boot中一个不起眼的好工具StopWatch