Index Hint是向优化器提供有关在查询处理期间如何选择索引。这里单存的索引选择上,不更改优化器策略。合理的索引可以加快数据检索操作,常用的索引Hint简单方式,使用USE(参考使用),IGNORE(忽略),FORCE(强制)三种方式。
- 但其语法上看,应该更精准的使用 JOIN,ORDER,GROUP 等场景。
index_hint:
USE {INDEX|KEY}
[FOR {JOIN|ORDER BY|GROUP BY}] ([index_list])
| {IGNORE|FORCE} {INDEX|KEY}
[FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
-
索引Hint应用于SELECT和UPDATE语句。还可以用于多表DELETE语句,但不能用于单表DELETE。
-
索引hint需要的是索引名,而不是列名,主键使用PRIMARY,其他的可以通过SHOW index语句或Information Schema STATISTICS查看。

常用方式
下面看下常用的方式。索引选择在干扰的情况下,执行计划的选择。
#1.优化器执行计划选择idx_fname索引
mysql> EXPLAIN SELECT * FROM employees
WHERE emp_no>10001 AND birth_date>'1965-01-01' AND first_name LIKE 'Ym%';
+----+-------------+-----------+------------+-------+--------------------------------+-----------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+--------------------------------+-----------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | employees | NULL | range | PRIMARY,idx_fname,idx_dt_birth | idx_fname | 62 | NULL | 260 | 0.31 | Using index condition; Using where |
+----+-------------+-----------+------------+-------+--------------------------------+-----------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)
#2.指定PRIMARY
mysql> EXPLAIN SELECT * FROM employees
USE INDEX (PRIMARY)
WHERE emp_no>10001 AND birth_date>'1965-01-01' AND first_name LIKE 'Ym%';
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | employees | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 149756 | 0.07 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
#3.忽略idx_fname索引
mysql> EXPLAIN SELECT * FROM employees
IGNORE INDEX (idx_fname)
WHERE emp_no>10001 AND birth_date>'1965-01-01' AND first_name LIKE 'Ym%';
+----+-------------+-----------+------------+-------+----------------------+--------------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+----------------------+--------------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | employees | NULL | range | PRIMARY,idx_dt_birth | idx_dt_birth | 3 | NULL | 1879 | 5.56 | Using index condition; Using where |
+----+-------------+-----------+------------+-------+----------------------+--------------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)
#4.强制idx_dt_birth索引
mysql> EXPLAIN SELECT * FROM employees
FORCE INDEX (idx_dt_birth)
WHERE emp_no>10001 AND birth_date>'1965-01-01' AND first_name LIKE 'Ym%';
+----+-------------+-----------+------------+-------+---------------+--------------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+--------------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | employees | NULL | range | idx_dt_birth | idx_dt_birth | 3 | NULL | 1879 | 3.70 | Using index condition; Using where |
+----+-------------+-----------+------------+-------+---------------+--------------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)
备注:可以说都按照干扰情况,选择了不同的索引。
多种组合使用场景
多个索引指定,排序,Join 指定。
#提示多个索引
mysql> EXPLAIN SELECT * FROM employees
USE INDEX (PRIMARY,idx_dt_birth)
WHERE emp_no>10001 AND birth_date>'1965-01-01' AND first_name LIKE 'Ym%' ;
+----+-------------+-----------+------------+-------+----------------------+--------------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+----------------------+--------------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | employees | NULL | range | PRIMARY,idx_dt_birth | idx_dt_birth | 3 | NULL | 1879 | 5.56 | Using index condition; Using where |
+----+-------------+-----------+------------+-------+----------------------+--------------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)
#JOIN提示
mysql>EXPLAIN SELECT a.*
FROM employees as a USE INDEX FOR JOIN (PRIMARY),
dept_emp as b USE INDEX FOR JOIN (idx_dept)
WHERE a.emp_no=b.emp_no;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------+
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 12 | 100.00 | NULL |
| 1 | SIMPLE | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | employees.b.emp_no | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------+
#ORDER BY指示
mysql> explain SELECT a.* FROM employees as a FORCE INDEX FOR JOIN (PRIMARY) IGNORE INDEX FOR ORDER BY(idx_fname),
-> dept_emp as b USE INDEX FOR JOIN (idx_emp)
-> WHERE a.emp_no=b.emp_no ORDER BY a.first_name;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+----------------------------------------------+
| 1 | SIMPLE | b | NULL | index | idx_emp | idx_emp | 4 | NULL | 12 | 100.00 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | employees.b.emp_no | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+----------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
USE&FORCE不允许同时使用
对于同一个张表来说 USE和FORCE 不能同时出现。
mysql> EXPLAIN SELECT a.* FROM employees as a
FORCE INDEX FOR JOIN (PRIMARY)
USE INDEX FOR ORDER BY(idx_fname);
ERROR 1221 (HY000): Incorrect usage of USE INDEX and FORCE INDEX
对于FULLTEXT索引
对于全文索引提示支持,但带有For ORDER BY或For GROUP BY的索引提示被默默忽略。
mysql> EXPLAIN SELECT *
FROM articles USE INDEX FOR ORDER BY (title) IGNORE index(PRIMARY)
WHERE MATCHH (title,body) AGAINST ('database' IN NATURAL LANGUAGE MODE)
ORDER BY title;
+----+-------------+----------+------------+----------+---------------+-------+---------+-------+------+----------+-------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+----------+---------------+-------+---------+-------+------+----------+-------------------------------+
| 1 | SIMPLE | articles | NULL | fulltext | title | title | 0 | const | 1 | 100.00 | Using where; Ft_hints: sorted |
+----+-------------+----------+------------+----------+---------------+-------+---------+-------+------+----------+-------------------------------+
1 row in set, 1 warning (0.00 sec)
对于DELETE语句
索引hint对于delete 语句也有要求,单表不支持,多表支持。
#单表操作不支持
mysql> EXPLAIN DELETE FROM employees USE INDEX (PRIMARY)
WHERE emp_no=10001;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'USE INDEX (PRIMARY) WHERE emp_no=10001' at line 1
#多表操作支持
mysql> EXPLAIN DELETE a
FROM employees as a USE INDEX FOR JOIN (PRIMARY),
dept_emp as b USE INDEX FOR JOIN (idx_dept)
WHERE a.emp_no=b.emp_no;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------+
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 12 | 100.00 | NULL |
| 1 | DELETE | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | employees.b.emp_no | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
总结
因为MySQL是索引组织表,索引优化是SQL语句常用的主要必要手段。普遍情况下执行计划都是能合理选择最优的索引,进行检索。但也有例外。
在官方Note提示后期就会弃掉,融合到Optimizer Hints里。要是在代码中使用索引Hint就要注意了。
说明:
USE INDEX、FORCE INDEX和IGNORE INDEX将在MySQL的未来版本中被弃用,并在之后的某个时间被完全删除。因为从MySQL 8.0.20开始,服务器支持索引级优化器提示JOIN_INDEX、GROUP_INDEX、ORDER_INDEX和INDEX,它们等价于并打算取代FORCE INDEX索引提示,以及NO_JOIN_INDEX、NO_GROUP_INDEX、NO_ORDER_INDEX和NO_INDEX优化器提示,它们等价于并打算取代IGNORE INDEX索引提示。

参考
https://dev.mysql.com/doc/refman/8.0/en/index-hints.html




