MySQL强制索引中USE/FORCE INDEX用法与避坑

 更新时间:2026年03月10日 09:26:13   作者:Hoffer_  
本文主要介绍了MySQL强制索引中USE/FORCE INDEX用法与避坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

MySQL 的查询优化器会根据统计信息(如基数、数据分布)自动选择它认为 “最优” 的索引。但有时它的判断可能不准,这时就需要我们手动干预,这时候就用到这两个语法:

  • USE INDEX:给优化器一个 “建议列表”,告诉它 “你可以从这些索引里选一个”,但它最终可能还是不采纳。
  • FORCE INDEX:给优化器一个 “强制命令”,告诉它 “你必须用这个索引”,没有商量余地。

一、USE INDEX 语法

SELECT *
FROM customer
USE INDEX (idx_last_name_first_name)  -- 放在 FROM 子句之后
WHERE last_name = 'BARBEE';
  • USE INDEX 后面跟着一个索引名列表,优化器只能从这个列表里选择。
  • 如果想让优化器忽略某些索引,可以用 IGNORE INDEX。

1、关键区别:USE(建议) vs FORCE(强制)

特性USE INDEXFORCE INDEX
性质建议(Hint)强制(Force)
优化器态度可以采纳,也可以忽略必须执行,没有选择
适用场景优化器选错索引,但你有更好的候选优化器完全不使用索引,导致性能极差
风险低,只是提供选项高,强制使用可能导致更差的性能

2、实战场景:什么时候用?

>>DESC customer;
+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+
| Field       | Type              | Null | Key | Default           | Extra                                         |
+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+
| customer_id | smallint unsigned | NO   | PRI | NULL              | auto_increment                                |
| store_id    | tinyint unsigned  | NO   | MUL | NULL              |                                               |
| first_name  | varchar(45)       | NO   |     | NULL              |                                               |
| last_name   | varchar(45)       | NO   | MUL | NULL              |                                               |
| email       | varchar(50)       | YES  |     | NULL              |                                               |
| address_id  | smallint unsigned | NO   | MUL | NULL              |                                               |
| active      | tinyint(1)        | NO   |     | 1                 |                                               |
| create_date | datetime          | NO   |     | NULL              |                                               |
| last_update | timestamp         | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |
+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+
9 rows in set (0.00 sec)

场景一:优化器选错了索引

在创建idx_last_name 和 idx_last_name_first_name 两个索引后,

CREATE INDEX idx_last_name
ON customer (last_name);
CREATE INDEX idx_last_name_first_name
ON customer (last_name, first_name);

用 EXPLAIN 语句查看以下查找姓氏为 BARBEE 的语句的执行计划,

EXPLAIN
SELECT *
FROM customer
WHERE last_name = 'BARBEE';

但发现使用 idx_last_name_first_name 更好.

EXPLAIN
SELECT * 
FROM customer
USE INDEX(id_last_name_first_name)
WHERE last_name = 'BARBEE';

就像例子里的情况:

  • 表 customer 有两个索引:idx_last_name 和 idx_last_name_first_name。
  • 查询 WHERE last_name = 'BARBEE' 时,优化器选了 idx_last_name。
  • 但你通过分析,认为 idx_last_name_first_name 更适合后续的排序或覆盖索引需求。
  • 这时用 USE INDEX (idx_last_name_first_name) 来引导它。

场景二:优化器完全不用索引

当你的查询条件明明有索引,但优化器因为统计信息过时等原因,选择了全表扫描,导致查询极慢。这时就需要用 FORCE INDEX 来强制它使用索引。

二、FORCE INDEX 语法

MySQL 查询优化器会根据统计信息(如数据分布、基数)自动选择执行计划。但在某些情况下,它的判断可能 “短视”,导致性能不佳。所以FORCE INDEX 就是用来强制它必须使用你指定的索引。

这里有一个film表,显示其索引(配合下文浏览即可):

+-------+------------+-----------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name                    | Seq_in_index | Column_name          | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+-----------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| film  |          0 | PRIMARY                     |            1 | film_id              | A         |        1000 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| film  |          1 | idx_title                   |            1 | title                | A         |        1000 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| film  |          1 | idx_fk_language_id          |            1 | language_id          | A         |           1 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| film  |          1 | idx_fk_original_language_id |            1 | original_language_id | A         |           1 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-------+------------+-----------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
4 rows in set (0.00 sec)

1、为什么优化器“不听话”?

案例:在film表查找语言为英语的影片(id = 1):

SELECT *
FROM film
WHERE language_id = 1;

但在查看这个语句的执行计划的时候(用EXPLAIN语句)——

发现MySQL 查询优化器并没有使用 idx_fk_language_id 索引。这是因为 film 表中的所有影片都是英文影片,因此 MySQL 查询优化器指定全表扫描。

所以在这个例子中的 film 表:

  • 表中有 idx_fk_language_id 索引,查询条件也是 WHERE language_id = 1。
  • 但优化器选择了全表扫描(type: ALL),因为它发现表中几乎所有行的 language_id 都是 1(英文电影)。
  • 对它来说,全表扫描比走索引更快,因为索引回表的开销超过了收益!!!

优化器的逻辑是:当查询需要返回大部分数据时,全表扫描可能更高效,比走索引 + 回表的方式更快。

那么如果我们要用FORCE INDEX强制索引:

EXPLAIN
SELECT * 
FROM film
FORCE INDEX (id_fk_language_id) -- FORCE INDEX 后面跟着一个索引名列表,优化器必须从这个列表中选择一个; 当然如果列表中的索引不可用,查询会报错。
WHERE language_id = 1;

这个例子只是为了举例而用FORCE INDEX...

2、避坑

  1. 不要滥用:优先让优化器自己做决定,只有在确认它判断错误时才手动干预。
  2. 验证性能:强制使用索引后,一定要用实际执行时间来验证性能是否真的提升了。但在这个例子中,强制使用索引反而可能更慢,因为需要回表读取所有行。
  3. 覆盖索引:如果查询的所有列都在索引中(覆盖索引),强制使用索引通常是有益的;如果需要回表,就要谨慎。
  4. 使用前务必用 EXPLAIN 分析,使用后务必验证性能。

到此这篇关于MySQL强制索引中USE/FORCE INDEX用法与避坑的文章就介绍到这了,更多相关MySQL强制索引内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL中修改表结构时需要注意的一些地方

    MySQL中修改表结构时需要注意的一些地方

    这篇文章主要介绍了MySQL中修改表结构时需要注意的一些地方,作者援引Percona的相关的说明来讲述如何避免相关操作导致表无法使用的问题,一些需要的朋友可以参考下
    2015-06-06
  • Mysql中JSON字段的值的实现示例

    Mysql中JSON字段的值的实现示例

    本文中介绍了如何通过SQL语句查询JSON字段中的特定数据,如查询数组中的元素,提取映射中的值,以及使用不同的JSON函数来处理数据,感兴趣的可以了解一下
    2024-09-09
  • 通过代码实例了解页面置换算法原理

    通过代码实例了解页面置换算法原理

    这篇文章主要介绍了通过代码实例了解页面置换算法原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • mysql全文搜索 sql命令的写法

    mysql全文搜索 sql命令的写法

    首先,大家先去下载一份dvbbs.php beta1的代码,解压后先抛开php代码,找出你的mysql手册,如果没有手册那么就直接看下面的实例操作吧!
    2011-01-01
  • MySQL实战之Insert语句的使用心得

    MySQL实战之Insert语句的使用心得

    这篇文章主要给大家介绍了关于MySQL实战之Insert语句的使用心得的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • MySQL 5.5, 5.6, 5.7, 8.0 特性对比

    MySQL 5.5, 5.6, 5.7, 8.0 特性对比

    MySQL是一个广泛使用的关系型数据库管理系统,你知道各个版本之前的区别吗,本文主要介绍了MySQL 5.5, 5.6, 5.7, 8.0 特性对比,具有一定的参考价值,感兴趣的可以了解一下
    2024-04-04
  • Linux安装mysql并配置外网访问的实例

    Linux安装mysql并配置外网访问的实例

    今天小编就为大家分享一篇Linux安装mysql并配置外网访问的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • mysql数据库基本语法及操作大全

    mysql数据库基本语法及操作大全

    这篇文章主要介绍了mysql数据库基本语法及操作大全,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • MySQL GROUP_CONCAT限制解决方案

    MySQL GROUP_CONCAT限制解决方案

    这篇文章主要介绍了MySQL GROUP_CONCAT限制解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
    2020-09-09
  • MySQL系列之八 MySQL服务器变量

    MySQL系列之八 MySQL服务器变量

    其中有些参数支持运行时修改,会立即生效;有些参数不支持,且只能通过修改配置文件,并重启服务器程序生效;有些参数作用域是全局的,且不可改变;有些可以为每个用户提供单独(会话)的设置
    2021-07-07

最新评论