目 录CONTENT

文章目录

【原理篇】聊聊MySQL优化器提示(optimizer hints)

冰点
2025-07-02 / 0 评论 / 0 点赞 / 22 阅读 / 22404 字

今天我们聊聊 MySQL 8.0 中的优化器提示功能,为什么会有MySQL 优化器提示呢。 大家可能之前都遇到过慢SQL

,并且惊讶的发现 MySQL 选错了索引或者当表数据发生大量变更后,优化器依赖的索引选择性、表行数等统计信息可能不再准确,导致它选错索引。为了解决这个问题,MySQL 引入了一种优化器提示功能,通俗的说,就是告诉MySQL Server 在执行SQL的时候 如何选择索引。

具体原文在官网的10.9.3 章节。https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html

什么时候使用优化器提示

  1. 定位问题:先用EXPLAIN发现优化器选错了执行计划(如该用索引却全表扫描)。

  2. 选对提示:根据问题选对应的提示(如INDEX_MERGE、BKA)。

  3. 验证效果:再用EXPLAIN看执行计划是否改变,并用SET TIMING ON对比查询耗时。

一句话总结优化器提示是“人工干预数据库执行策略”的工具,能用在优化器“犯糊涂”时拉它一把,但别把它当成替代合理索引设计的“万能药”。


优化器提示(Optimizer Hints)是数据库领域的重要功能,不同数据库管理系统引入该功能的版本存在差异。以下为你介绍主流数据库中优化器提示的引入版本及相关内容:

引入版本

  • MySQL 5.6首次引入了部分优化器提示功能,但功能相对有限。MySQL 8.0对优化器提示进行了大幅增强,支持更多类型的提示,使其更灵活和实用。

1. 常见优化器提示示例

在 MySQL 中,优化器提示通过 /*+ */ 语法添加到 SQL 语句中,例如:

SELECT /*+ HINT_NAME */ column1, column2
FROM table1
JOIN table2 ON table1.id = table2.id;
  • USE INDEX:指定查询使用的索引

    SELECT * FROM orders /*+ USE INDEX(idx_order_date) */ WHERE order_date > '2023-01-01';
    
  • IGNORE INDEX:忽略指定的索引

    SELECT * FROM customers /*+ IGNORE INDEX(idx_customer_name) */ WHERE customer_name LIKE 'A%';
    
  • FORCE INDEX:强制使用指定的索引

    SELECT * FROM products /*+ FORCE INDEX(primary) */ WHERE product_id = 100;
    

2. 核心本质用提示覆盖优化器的默认决策

数据库优化器会根据统计信息(如索引选择性、表大小)自动选择执行计划,但它的判断可能出错:

  • 场景1:索引存在但优化器没选(如统计信息过时)。

  • 场景2:多个索引可用,优化器选了低效的那个。

  • 场景3:需要临时修改数据库行为(如禁用外键检查)。

优化器提示的作用就是:强制指定执行策略,覆盖优化器的自动选择。

3. 用提示“指挥”数据库干活

unsetunset例子:查“25-35岁”或“活跃”的用户(索引合并场景)unsetunset

假设表结构:

CREATE TABLE users (
  id INT PRIMARY KEY,
  age INT,
  status VARCHAR(20),
  -- 索引:年龄和状态各有一个索引
  KEY idx_age(age),
  KEY idx_status(status)
);
unsetunset1. 无提示:优化器可能的选择unsetunset
-- 正常查询:优化器可能选全表扫描或单个索引
SELECT * FROM users WHERE age BETWEEN 25 AND 35 OR status = 'active';

问题:如果age和status的条件都有很多结果,单个索引可能效率低,优化器却没意识到可以合并索引。

unsetunset2. 加提示:强制合并两个索引unsetunset
-- 用INDEX_MERGE提示告诉优化器:“合并两个索引扫描!”
SELECT /*+ INDEX_MERGE(users idx_age, idx_status) */ * 
FROM users 
WHERE age BETWEEN 25 AND 35 OR status = 'active';

执行过程:

  • 优化器被迫同时扫描idx_age(查25-35岁)和idx_status(查活跃用户)。

  • 把两个结果合并(类似“OR”的并集),再取数据,比单个索引更高效。

4. 常见提示类型解决不同场景

提示类型

作用场景

示例

索引强制

让优化器必须用某个索引(避免选错索引)

/*+ INDEX(users idx_age) */

索引禁用

让优化器忽略某个索引(如索引低效时)

/*+ NO_INDEX(users idx_age) */

连接算法

指定连接方式(如BKA提升JOIN效率)

/*+ BKA(t1) */(对表t1用批量键访问算法)

临时变量

临时修改数据库变量(如禁用外键检查)

/*+ SET_VAR(foreign_key_checks=OFF) */

执行时间

限制查询最长执行时间(防止慢查询拖垮数据库)

/*+ MAX_EXECUTION_TIME(5000) */(5秒后强制终止)

6. 如何验证提示生效?—— 用EXPLAIN查看数据库的执行计划

EXPLAIN SELECT /*+ INDEX_MERGE(users idx_age, idx_status) */ * 
FROM users 
WHERE age BETWEEN 25 AND 35 OR status = 'active';
  • type列显示index_merge → 确认使用索引合并。

  • key列显示idx_age,idx_status → 确认用了指定的两个索引。

  • Extra列显示Using union(...) → 确认是“OR”条件的并集合并。

0

评论区