一、MySQL查询优化器是什么?

MySQL的查询优化器就像是一个智能导航系统,当你写了一条SQL语句,它负责帮你选择最快的执行路径。不过有时候,这个"导航"也会犯迷糊,比如明明有高速路不走,偏要绕乡间小路。这时候就需要我们给它一些"提示",让它回到正轨。

举个例子,假设我们有个订单表orders和用户表users,要查某个用户的订单:

-- 没有优化的查询
EXPLAIN SELECT * FROM orders 
JOIN users ON orders.user_id = users.id 
WHERE users.name = '张三';

你可能发现优化器选择了全表扫描users,而不是用索引。这时候就需要我们出手干预了。

二、常用的优化器提示技巧

1. FORCE INDEX:强制走索引

当优化器死活不肯用索引时,可以用这个"强硬手段":

-- 强制使用name索引
SELECT * FROM users FORCE INDEX(idx_name) 
WHERE name = '张三';

注意:这就像强迫导航走指定路线,如果路况实际很差(比如索引区分度低),反而会更慢。

2. STRAIGHT_JOIN:控制连接顺序

在多表关联时,优化器可能会选错驱动表。比如:

-- 让orders作为驱动表
SELECT STRAIGHT_JOIN orders.* FROM orders 
JOIN users ON orders.user_id = users.id 
WHERE users.status = 1;

适用场景:当你知道小表驱动大表效率更高时。

3. SQL_BIG_RESULT / SQL_SMALL_RESULT

告诉优化器结果集的大小预期:

-- 预期结果很大,优先用磁盘排序
SELECT SQL_BIG_RESULT * FROM logs 
WHERE create_time > '2023-01-01' 
ORDER BY id;

三、高级玩法:优化器开关

MySQL还提供了"调节旋钮",通过设置优化器开关来改变其行为:

-- 关闭某些优化策略
SET optimizer_switch='block_nested_loop=off';

常见开关包括:

  • index_merge:是否允许索引合并
  • mrr:是否使用多范围读取优化
  • derived_merge:是否合并派生表

四、实战案例分析

假设有个电商数据库,我们要查询热销商品:

-- 原始低效查询
EXPLAIN SELECT p.* FROM products p 
JOIN order_items oi ON p.id = oi.product_id 
WHERE p.category = '电子产品' 
GROUP BY p.id 
ORDER BY COUNT(*) DESC LIMIT 10;

优化步骤

  1. 确认categoryproduct_id是否有索引
  2. 使用FORCE INDEX确保分组走正确的索引
  3. 添加SQL_BIG_RESULT提示

最终优化版:

SELECT SQL_BIG_RESULT p.* FROM products p FORCE INDEX(idx_category) 
JOIN order_items oi FORCE INDEX(idx_product_id) 
ON p.id = oi.product_id 
WHERE p.category = '电子产品' 
GROUP BY p.id 
ORDER BY COUNT(*) DESC LIMIT 10;

五、注意事项与总结

注意事项:

  1. 不要滥用提示:优化器通常比你更懂数据分布
  2. 版本差异:不同MySQL版本优化器行为可能不同
  3. 监控效果:每次修改后要用EXPLAIN验证

适用场景:

  • 统计报表类复杂查询
  • 紧急修复线上慢SQL
  • 索引区分度极高的字段查询

技术优缺点:

✅ 精准控制执行计划
✅ 快速解决特定性能问题
❌ 增加SQL维护成本
❌ 数据变化后可能失效

总结:优化器提示就像汽车的手动模式,在自动挡失灵时能救命,但老司机也不该天天用。理解其原理,才能做到"该出手时才出手"。