复合索引需严格遵循最左前缀匹配原则,如index(status, created_at)无法用于where created_at > '2024-01-01' and status = 'active';索引字段上使用函数或表达式(如date(created_at))也会导致索引失效。
索引字段顺序不匹配 where 条件顺序
复合索引不是“包含这些列就行”,而是严格依赖最左前缀匹配。比如建了 index(status, created_at),但查询写的是 where created_at > '2024-01-01' and status = 'active',mysql 优化器大概率不会用这个索引——created_at 不在最左,无法跳过 status 直接定位。
- 检查执行计划:右键 SQL → “解释”,看
key列是否为空、possible_keys是否有值但未被选用 - 调整索引顺序:把高频等值条件字段放最左,范围查询字段(如
BETWEEN、>)放右侧 - 避免“猜索引”:用
SHOW INDEX FROM table_name确认实际索引字段顺序,别只看命名
索引列上用了函数或表达式
只要 WHERE 中对索引字段做了任何计算或类型转换,索引就立即失效。典型例子:WHERE DATE(created_at) = '2024-06-01' 或 WHERE user_id + 0 = 12345 —— 这些都会触发全表扫描,而 Navicat 渲染大量结果时卡顿更明显。
- 改写为范围查询:
created_at >= '2024-06-01' AND created_at - 避免隐式转换:确保参数类型和字段类型一致,比如
user_id是BIGINT,就别传字符串'12345' - 留意 COLLATION 影响:字符字段用
utf8mb4_0900_as_cs时,大小写敏感比较可能绕过索引
Navicat 默认拉取全部结果再本地截断
这是最容易被误判为“索引没用”的场景:你写了 SELECT * FROM orders WHERE status = 'shipped' LIMIT 10,看起来只想要 10 行,但 Navicat(尤其老版本)默认会先请求服务端返回所有匹配行,再在本地做 LIMIT 和语法高亮。如果 status = 'shipped' 匹配 50 万行,网络+内存+GUI 渲染全压在客户端。
- 进连接设置 → 高级 → 勾选
Limit rows并设为 100(别留空) - 禁用自动刷新:
Auto-refresh result set关掉,翻页时不再重复执行 - 导出大数据量时,务必选
Server export而非Client export,让 MySQL 自己生成文件
统计信息陈旧导致优化器选错索引
MySQL 依赖表的统计信息(如索引基数、数据分布)决定是否用某个索引。如果大批量 INSERT/DELETE 后没更新统计信息,优化器可能认为某索引“选择性差”,宁愿走全表扫描——这时加的索引不仅没提速,还因维护开销拖慢写入。
- 手动更新:
ANALYZE TABLE orders(注意:会锁表,建议低峰期) - 检查实际基数:
SELECT index_name, seq_in_index, column_name, cardinality FROM information_schema.STATISTICS WHERE table_name = 'orders' - 确认
innodb_stats_auto_recalc是否开启(MySQL 5.6.6+ 默认 ON),否则大表变更后不会自动更新
真正卡住的地方往往不在“有没有索引”,而在“索引能不能被看见、被选中、被安全使用”。Navicat 的 GUI 行为会放大底层决策失误,所以每次怀疑索引失效,先看执行计划里 key 是不是真为空,再查 Navicat 是否偷偷把 LIMIT 当摆设。











