胖乎乎的 SQL 查询就像一个不爱运动的宅男,消耗大量资源却动作缓慢...是时候给它们制定一份健身计划了!
查询优化是指通过改写 SQL 语句和利用索引等手段,提高查询执行效率,减少资源消耗的过程。简单来说:就是让你的 SQL 语句"减肥",从臃肿的胖子变成精壮的肌肉男。
在给 SQL 制定"健身计划"前,我们需要先了解它的"身体状况",这就需要用到 EXPLAIN 命令。
EXPLAIN SELECT * FROM employees WHERE salary > 50000;
这条命令会告诉你查询是如何执行的,就像医生给你的体检报告一样详细!
字段名 | 相当于体检中的... | 理想状态 | 问题状态 |
---|---|---|---|
seleC++t_type | 检查类型 | SIMPLE (简单查询) | SUBQUERY (子查询,可能有嵌套问题) |
table | 检查对象 | 实际表名 | (嵌套衍生表,消化不良?) |
type | 访问方法 | const > eq_ref > ref | ALL (全表扫描,相当于全身脂肪率过高!) |
possible_keys | 可用工具 | 可能的索引列表 | NULL (没有可用索引,光靠"徒手"健身) |
key | 实际使用工具 | 实际使用的索引 | NULL (最终没用上任何索引,赤膊上阵) |
rows | 检查样本量 | 越少越好 | 数值巨大 (需要翻很多行才能找到,太累了) |
Extra | 额外说明 | Using index (只用索引) | Using filesort (需要额外排序,相当于多做了俯卧撑) |
-- 肥胖的SQL
SELECT * FROM customers WHERE name LIKE '%John%';
问题: 使用了模糊前缀匹配,无法利用索引,必须扫描全表
体检结果: type = ALL
,扫描所有行,就像用肉眼在人山人海中找一个叫"John"的人
-- 贪吃的SQL
SELECT * FROM orders WHERE customer_id = 1001;
问题: 使用SELECT *
获取了不必要的列数据
体检结果: 传输数据量过大,网络和内存消耗增加
-- 好大喜功的SQL
SELECT * FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN customers c ON o.customer_id = c.customer_id
JOIN employees e ON o.employee_id = e.employee_id
WHERE o.order_date > '2023-01-01';
问题: 过多的表 JOIN,每增加一个表就增加复杂度
体检结果: rows
值巨大,可能是多个表行数的乘积,就像一次性想练全身肌肉群
-- 添加合适的索引
CREATE INDEX idx_customer_name ON customers(name);
-- 使用精确匹配让索引发挥作用
SELECT * FROM customers WHERE name = 'John Smith';
健身效果: type = ref
,直接利用索引找到匹配行,就像知道对方座位号直接找人
-- 只取需要的列
SELECT order_id, customer_id, order_date
FROM orders
WHERE customer_id = 1001;
健身效果: 减少数据传输量,就像只吃鸡胸肉和西兰花,不要垃圾食品
-- 使用LIMIT控制返回行数
SELECT * FROM products ORDER BY price DESC LIMIT 10;
健身效果: 只处理部分数据,避免一次性返回大量结果,就像把马拉松分解成多个小跑步
-- 优化前
SELECT * FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_date > '2023-01-01' AND oi.quantity > 5;
-- 优化后(条件下推)
SELECT * FROM orders o
JOIN (SELECT * FROM order_items WHERE quantity > 5) oi
ON o.order_id = oi.order_id
WHERE o.order_date > '2023-01-01';
健身效果: 提前过滤数据,减少 JOIN 处理的数据量,就像健身前先做热身,提高效率
-- 错误示范
SELECT * FROM orders WHERE order_id = '1001';
问题: 字符串和数字比较导致隐式转换,无法使用索引
解决方法: 使用正确的数据类型进行比较
-- 错误示范
SELECT * FROM employees WHERE YEAR(hire_date) = 2023;
问题: 在索引列上使用函数会导致无法使用索引
解决方法: 改写 SQL,让索引列保持原样
-- 肥胖的SQL
SELECT * FROM customers WHERE name LIKE '%John%';
0
-- 肥胖的SQL
SELECT * FROM customers WHERE name LIKE '%John%';
1
问题: 长时间持有锁,影响并发
解决方法: 将大事务拆分成小事务,减少锁定时间
-- 肥胖的SQL
SELECT * FROM customers WHERE name LIKE '%John%';
2
-- 肥胖的SQL
SELECT * FROM customers WHERE name LIKE '%John%';
3
健身效果: Extra = Using index
,只使用索引就能获取所有需要的数据,不需要回表查询原始数据,就像一个动作锻炼多个肌肉群
永远用 EXPLAIN - 没有体检报告的健身都是盲目的
定期检查慢查询日志 - 就像体重秤,发现哪些 SQL"发福"了
减少数据访问量 - 无论是行数还是列数,都是"少即是多"
正确使用索引 - 索引是 SQL 健身的"蛋白粉",用对了事半功倍
避免使用 SELECT * - 这就像去自助餐厅盛满盘子却吃不完,浪费资源
"优化 SQL 查询就像健身一样,不需要追求完美,但需要持续改进。一个看起来很丑但执行很快的 SQL,比一个优雅但速度慢如蜗牛的 SQL 要好得多。"
—— 匿名数据库性能专家
下次面试官问你 MySQL 查询优化,不要害怕!告诉他:那不过是给 SQL 安排一次健康减肥而已!