无索引where字段触发全表扫描,复合索引需遵循最左匹配原则,select *导致覆盖索引失效,函数/类型转换/or会隐式使索引失效。

WHERE条件字段没索引,直接全表扫描
只要查询里有 WHERE,而对应列没索引,MySQL 就大概率走 ALL 类型的全表扫描。比如 SELECT * FROM orders WHERE customer_id = 123,如果 customer_id 没索引,哪怕表只有 10 万行,也可能扫几秒。
实操建议:
- 用
EXPLAIN看key是否为NULL、type是否为ALL—— 这俩同时出现,基本就是没走索引 - 高频过滤字段(如状态、用户ID、时间戳)优先建单列索引
- 别等慢了才加:上线前就根据核心接口的
WHERE条件预建索引
复合索引列顺序写反,后边全作废
联合索引 (a, b, c) 只能高效支持 WHERE a = ?、WHERE a = ? AND b = ?、WHERE a = ? AND b = ? AND c > ? 这类查询;但 WHERE b = ? 或 WHERE a = ? AND c = ? 就几乎用不上。
原因很简单:B+ 树按最左列排序,跳过首列等于在目录里随机翻页。
实操建议:
- 严格按「等值 → 范围 → 排序」排:比如查询是
WHERE status = 'paid' AND amount > 100 ORDER BY created_at,索引就该建(status, amount, created_at) - 范围条件(
>、BETWEEN、LIKE 'abc%')之后的列,索引失效 —— 别指望它还能加速ORDER BY或SELECT字段 - 如果真要单独查
created_at,就额外建个单列索引,别硬塞进联合索引末尾
SELECT * 让覆盖索引失效,回表开销爆炸
SELECT * 本身不导致索引失效,但它让「覆盖索引」彻底没用 —— 因为索引里存不下整行数据,MySQL 必须回到主键索引逐行取,产生大量随机 IO。
当预估返回行数超过总行数 20%~30%,优化器甚至会主动放弃索引,改走全表扫描(顺序 IO 更快)。
实操建议:
- 业务代码里禁止无脑
SELECT *,明确写出需要的字段,比如SELECT user_id, name, email - 把
SELECT字段也加进索引末尾,做成覆盖索引:对SELECT user_id, name FROM users WHERE status = 'active',建INDEX idx_status_cover (status, user_id, name) - 检查
EXPLAIN输出的Extra字段:出现Using index才算真正覆盖,Using where; Using index condition是半覆盖,仍需回表
函数、类型转换、OR 条件悄悄干掉索引
这些操作看着不起眼,但会让优化器当场放弃索引:
-
WHERE DATE(create_time) = '2024-01-01'→ 改成WHERE create_time >= '2024-01-01' AND create_time -
WHERE phone = 13812345678(phone 是VARCHAR)→ 改成WHERE phone = '13812345678' -
WHERE name = 'A' OR age = 25(age 无索引)→ 拆成两个查询UNION,或给age单独建索引
MySQL 8.0+ 虽支持函数索引(如 CREATE INDEX idx_phone_upper ON users ((UPPER(phone)))),但只应在刚需场景用,别把它当成兜底方案。
真正难搞的是隐式转换和模糊匹配 —— 它们不会报错,只会默默变慢,而且很难被监控发现。










