WHERE条件未走索引主因是索引失效:隐式类型转换、索引列用函数、联合索引缺失最左前缀、IS NULL在部分版本不走索引;InnoDB聚簇索引需回表,MyISAM索引存物理地址;MEMORY仅适用于临时数据;联合索引顺序应按查询中WHERE和ORDER BY字段组合确定。

为什么 WHERE 条件用了字段却没走索引?
常见现象是执行 EXPLAIN SELECT * FROM t WHERE status = 'active'; 显示 type=ALL,即全表扫描。根本原因往往不是没建索引,而是索引失效:
-
status字段类型为VARCHAR,但查询时写了WHERE status = 1(隐式类型转换) - 在索引列上用了函数,比如
WHERE UPPER(name) = 'JOHN' - 联合索引
(a,b,c),只用WHERE b = ?或WHERE a = ? AND c = ?(缺少最左前缀) - 索引列允许
NULL,且查询条件是IS NULL,某些 MySQL 版本下无法使用索引(尤其MyISAM)
验证方式:用 SHOW INDEX FROM t; 看索引结构,再用 EXPLAIN FORMAT=TRADITIONAL 查看实际是否命中。
InnoDB 和 MyISAM 在索引设计上的关键差异
二者 B+ 树组织方式不同,直接影响你建什么索引、怎么写查询:
-
InnoDB主键即聚簇索引,数据行直接存于主键 B+ 树的叶子节点;二级索引叶子存的是主键值,回表成本真实存在 -
MyISAM所有索引都是非聚簇的,叶子节点存的是行物理地址(.MYD 文件偏移),没有“回表”概念,但不支持事务和行锁 - 如果你频繁按
user_id查询并需要返回大量字段,InnoDB下把user_id设为主键或建覆盖索引(如INDEX idx_uid_name_age (user_id, name, age))能避免回表 -
MyISAM的COUNT(*)很快(内部维护行数),但InnoDB必须扫索引树——所以不要在大表上无条件用SELECT COUNT(*)
什么时候该用 MEMORY 引擎?别只看“快”
MEMORY 表数据全在内存,SELECT 极快,但极易误用:
- 只适合临时中间结果、缓存维度表(如城市字典)、或秒级生命周期的会话数据
- 不支持
TEXT/BLOB类型,也不支持外键和事务 - 服务器重启后数据全丢——如果误把用户订单表设为
MEMORY,就是生产事故 - 默认用哈希索引(
HASH),只支持等值查询(=、);范围查询(BETWEEN、>)必须显式声明USING BTREE
CREATE TABLE tmp_user_cache ( id BIGINT PRIMARY KEY, name VARCHAR(64), updated_at DATETIME ) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 AVG_ROW_LENGTH=128;
联合索引字段顺序到底怎么排?看查询模式,不是看字段重要性
错误认知:“把区分度高的字段放前面”。真正决定顺序的是 WHERE 和 ORDER BY 的组合模式:
- 如果常查
WHERE category = ? AND status = ? ORDER BY created_at DESC,索引应为(category, status, created_at),而非反过来 - 如果还有
WHERE category = ? ORDER BY status ASC,那(category, status)就比单列category更有效 - 注意
ORDER BY方向一致性:(a ASC, b DESC)在 MySQL 8.0+ 才支持,旧版本会忽略b的排序,降级为文件排序(Using filesort) - 索引总长度别超限制:
InnoDB单索引前缀最大 3072 字节(utf8mb4下约 768 个字符),超了会截断,导致后缀字段失效
最常被忽略的一点:索引不是越多越好。每个写操作都要更新所有相关索引,高并发写入场景下,5 个索引可能比 1 个慢 3 倍以上——先看慢查询,再加索引,别预设。










