force index 的正确打开方式以及它的三个代价遇到慢 SQL很多人的第一反应是用force index指定一个索引。写法简单改完立刻见效所以很受欢迎。但我的观点是**force index 是“最后一招”不是“第一招”。**它只是让优化器闭嘴没有回答最重要的问题它为什么没选你想要的索引下面先讲基本用法再讲五个我认为更重要的判断为什么没走、走索引不等于快、强制索引的代价、该用哪种提示以及大表 count 慢的真正原因。01基本用法先 explain再决定第一步永远是用explain分析 SQL看优化器实际选了什么explain select count(id) from person_info_large;重点看几列type访问类型、key实际使用的索引、rows预估扫描行数、Extra如Using index、Using filesort、Using temporary。如果想让 SQL 走指定的索引可以用force index主键索引的名字固定叫PRIMARYexplain select count(id) from person_info_large force index (primary);改完再 explain 一次对比前后key和rows的变化。02观点一先查“为什么没走”再谈“强制走”优化器没选你想要的索引通常有原因。强制索引前先排除这几类原因表现正确的处理统计信息过期预估行数和实际差很多选错索引对表执行ANALYZE TABLE更新统计信息索引选择性差索引列重复值很多优化器认为不如全表扫描换更有区分度的列或联合索引写法让索引失效列上用了函数、隐式类型转换、前导通配符like %xx改写 SQL让条件能用上索引索引设计不合理联合索引不满足最左前缀调整索引而不是调整提示这些原因里大部分改 SQL 或改索引就能根治。force index 对这些问题都是绕过不是修复。03观点二走了索引不等于更快这是原文最容易让人误解的地方。看到key列有值就以为问题解决了。但以count为例InnoDB 的主键索引是聚簇索引叶子节点存的是整行数据二级索引的叶子节点只存索引列加主键体积通常小得多。统计行数时优化器往往会倾向选择更小的二级索引去扫描因为读的页更少。判断如果你强制它走主键反而可能比优化器自己的选择更慢因为要扫描体积更大的聚簇索引。具体是否如此要看你的表结构和数据以实际耗时为准。做法别只看key要对比rows和真实执行时间。较新的 MySQL 8.0 版本支持explain analyze会给出实际执行耗时更适合验证。04观点三force index 的代价往往被低估取在优化器明显误判、线上要紧急止血时force index 是有效手段改动小、见效快。舍一写死了索引名以后索引改名、删除或重建SQL 会直接报错提示找不到该索引而且这种错误常常到上线后才暴露。舍二数据分布会变今天对的强制索引数据量或分布变化后可能变成最差的选择而优化器已经被你禁止重新判断。舍三难以维护散落在代码或 ORM 里的索引提示换个人接手没人知道为什么写也没人敢删。所以如果必须用请在代码旁写清楚原因、日期和验证结果并在文档里登记方便日后复查。05观点四分清三种提示用对工具写法含义适用FORCE INDEX (idx)强烈要求使用该索引除非实在无法使用否则不再考虑全表扫描优化器明显误判时USE INDEX (idx)建议优化器只在指定的索引中选择不保证一定使用想缩小选择范围但仍保留弹性IGNORE INDEX (idx)让优化器不要用某个索引某个索引总被错选MySQL 8.0 还支持 Optimizer Hint 语法写在/* ... */里如INDEX、NO_INDEX粒度更细也更便于按语句管理。具体语法请以你的 MySQL 版本文档为准。06观点五大表 count 慢是结构问题不是索引问题原文的例子是对一张大表执行count(id)。要明白InnoDB 不像 MyISAM 那样保存精确行数统计行数需要真实扫描。表越大count 越慢这是引擎的特性不是索引选错了。与其纠结用哪个索引不如换个思路近似值如果业务能接受估算可以用information_schema中的预估行数或show table status速度快但不精确。汇总表或计数器在写入时同步维护一个计数查询时直接读取。缓存用 Redis 缓存统计结果设置合理的过期或更新策略。加条件带过滤条件的 count让条件走上合适的索引缩小扫描范围。一句话总结遇到慢 SQL按这个顺序处理先explain看现状再查为什么没走索引优先改 SQL、改索引、更新统计信息最后才考虑 force index并用真实耗时验证、写清原因。强制索引不是答案只是止血。说明本文示例基于 MySQLInnoDB不同版本的优化器行为和语法可能有差异请以实际环境的 explain 结果和官方文档为准。用 force index()指定想要走的索引一、使用explain工具分析sqlexplain select count(id) from person_info_large;二、修改sql或者尽量让sql走索引explain select count(id) from person_info_largeforce index(primary);