MySQL 索引提示:USE INDEX

本文讲述如何在 MySQL 中使用 USE INDEX 建议查询优化器使用指定的命名索引。

MySQL 查询优化器是 MySQL 数据库服务器的一个组件,它为 SQL 语句制定最佳执行计划。 MySQL 优化器通常根据索引基数进行决策。 有时候,虽然你创建了索引,但是你的 SQL 语句却不一定使用索引。 这是因为 MySQL 查询优化器的做出了它认为的更优的选择。

MySQL 允许您使用 USE INDEX 语句建议查询优化器去使用指定的命名索引。

但是, MySQL 查询优化器依然有可能不适用您建议的索引。 如果您想 MySQL 必须使用您指定的索引,请使用 FORCE INDEX 子句。

EXPLAIN 显示查询优化器使用错误索引的情况下, USE INDEX 很有用。

MySQL USE INDEX 语法

要使用 MySQL USE INDEX 进行索引提示,请遵循以下语法:

SELECT column_list
FROM table_name
USE INDEX (index_list)
WHERE condition;

说明:

  • 请将 USE INDEX 子句放在 FROM 子句之后。
  • 如果 MySQL 查询优化器要使用索引,则必须使用索引列表 index_list 中的一个索引。

请注意,查询优化器不一定会使用您建议的命名索引。

MySQL USE INDEX 实例

我们将使用来自示例数据库中customer 表进行演示。

看一下 actor 表的定义:

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)

让我们创建两个索引,

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

看一下 customer 表的中的索引:

SHOW INDEXES FROM customer;
+----------+------------+--------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table    | Non_unique | Key_name                 | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+----------+------------+--------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| customer |          0 | PRIMARY                  |            1 | customer_id | A         |         599 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| customer |          1 | idx_fk_store_id          |            1 | store_id    | A         |           2 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| customer |          1 | idx_fk_address_id        |            1 | address_id  | A         |         599 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| customer |          1 | idx_last_name            |            1 | last_name   | A         |         598 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| customer |          1 | idx_last_name_first_name |            1 | last_name   | A         |         598 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| customer |          1 | idx_last_name_first_name |            2 | first_name  | A         |         599 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+----------+------------+--------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
6 rows in set (0.00 sec)

通过 EXPLAIN 语句查看以下查找姓氏为 BARBEE 的语句的执行计划:

EXPLAIN
SELECT *
FROM customer
WHERE last_name = 'BARBEE';
+----+-------------+----------+------------+------+----------------------------------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys                          | key           | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+----------------------------------------+---------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | customer | NULL       | ref  | idx_last_name,idx_last_name_first_name | idx_last_name | 182     | const |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+------+----------------------------------------+---------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

从输出可以发现, MySQL 查询优化器选择使用 idx_last_name 索引。

如果您认为使用 idx_last_name_first_name 更好,则使用 USE INDEX 指定它,如下:

EXPLAIN
SELECT *
FROM customer
USE INDEX (idx_last_name_first_name)
WHERE last_name = 'BARBEE';
+----+-------------+----------+------------+------+--------------------------+--------------------------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys            | key                      | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+--------------------------+--------------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | customer | NULL       | ref  | idx_last_name_first_name | idx_last_name_first_name | 182     | const |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+------+--------------------------+--------------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

请注意,这仅用于演示目的,但不是最佳选择。

从输出可以发现, MySQL 查询优化器选择使用 idx_last_name_first_name 索引。

结论

本文讨论了 MySQL USE INDEX 索引提示的基本用法。 USE INDEXFORCE INDEX 是不同的:

  • USE INDEX 告诉 MySQL 用列表中的其中一个索引去做本次查询,但是 MySQL 不一定会用。
  • FORCE INDEX 强制 MySQL 使用一个特定的索引。