FORCE INDEX 须写在 FROM 后且索引真实存在、字段顺序匹配查询条件,否则报错或退化;需结合 EXPLAIN FORMAT=TREE、Handler_read 统计和 OPTIMIZER_TRACE 验证实际执行效果。
MySQL 中 FORCE INDEX 怎么写才生效
直接加在 select 语句的 from 子句后,语法是 from table_name force index (index_name)。不是所有索引名都能用——必须是该表上真实存在、且字段顺序和查询条件匹配的索引,否则 mysql 会报错或静默退回到优化器选择。
-
FORCE INDEX不会跳过索引检查:如果查询条件里没用到该索引的最左前缀字段(比如索引是(a,b,c),但WHERE只写了c = 1),MySQL 仍可能拒绝使用它,甚至报ERROR 1176: Key 'xxx' doesn't exist in table 'yyy'(注意:这错误名容易误导,实际常因字段不匹配) - 复合索引要注意字段顺序:强制
FORCE INDEX (idx_a_b)时,WHERE a = 1 AND b > 10能走,但WHERE b = 5单独出现,基本无效 - 别漏掉反引号:索引名含特殊字符或关键字(如
order)时,必须写成FORCE INDEX (`order`)
为什么 FORCE INDEX 有时像没起作用
常见假象是执行计划里 key 列显示了索引名,但 rows 高得离谱,或者 Extra 出现 Using where; Using index 以外的冗余操作。本质是 MySQL 虽然“用了”你指定的索引,但没按你预期的方式用——比如回表次数爆炸、范围扫描覆盖全索引页。
- 检查
EXPLAIN FORMAT=TREE输出,看是否真走的是索引下推(ICP)还是退化为全索引扫描 - 对比
FORCE INDEX和不加时的filtered值:如果强制后filtered从 95% 掉到 5%,说明索引选择本身就不适合这个查询模式 - 注意隔离级别影响:在
REPEATABLE READ下,MVCC 版本链可能导致强制索引后仍要过滤大量旧版本行,看起来慢
FORCE INDEX 和 USE INDEX、IGNORE INDEX 的关键区别
三者都是提示(hint),但行为边界很不同:FORCE INDEX 是“必须用这个,否则报错”,而 USE INDEX 是“优先考虑这些,不行就换别的”,IGNORE INDEX 是“绝对不许碰这几个”。线上压测时误用 USE INDEX 容易掩盖问题,因为优化器悄悄切到全表扫也不会提醒你。
-
FORCE INDEX (a)在a字段无可用索引时会报错;USE INDEX (a)会直接忽略提示,继续选其他索引或全表扫 - 多个索引可同时指定:
FORCE INDEX (idx1, idx2)表示“只允许从这两个里选”,不是“都要用” - DDL 改动后记得同步更新 hint:比如把
idx_user_id改名为idx_uid,旧 SQL 里的FORCE INDEX (idx_user_id)就会失效报错
测试时怎么避免被缓存/执行计划干扰
强制索引效果验证最怕遇到预编译缓存或 Query Cache(虽 8.0 已移除,但低版本或 Proxy 层可能还有)。直接跑 EXPLAIN 看计划只是静态分析,必须配合 SELECT 实际执行 + SHOW PROFILE 或性能模式查真实 I/O 和 CPU 消耗。
- 每次测试前加
FLUSH STATUS,再执行查询,然后查SHOW STATUS LIKE 'Handler_read%'看物理读是否下降 - 用
SELECT SLEEP(0.001)这类无意义表达式“污染”SQL 文本,绕过语句级缓存(如某些中间件的 plan cache) - 在
information_schema.OPTIMIZER_TRACE开启后查 trace,确认considered_access_paths里是否真的排除了其他索引路径
status=1)、或统计信息陈旧时,FORCE INDEX 可能比优化器自己选的还差。真正要盯的是 rows_examined 和磁盘随机 I/O 次数,而不是执行计划里那个看似“漂亮”的索引名。










