select * 易触发全表扫描,因无合适索引时mysql需逐行读取数据页;配合无索引where条件、函数/隐式转换更致索引失效,应避免并用explain验证。

为什么 SELECT * 容易触发全表扫描
MySQL 在没有合适索引支撑时,会逐行读取数据页来匹配条件,这就是全表扫描。最典型的情况是写 SELECT * 配合 WHERE 条件但字段没建索引,或者 WHERE 中用了函数、类型隐式转换,导致索引失效。
实操建议:
- 用
EXPLAIN查看执行计划,重点看type列是否为ALL(表示全表扫描) - 避免在
WHERE子句中对索引列做运算或函数调用,例如WHERE YEAR(create_time) = 2023会让create_time索引失效 - 字符串比较注意类型一致:
WHERE user_id = '123'(user_id是INT)会触发隐式转换,可能放弃索引 - 尽量只查需要的字段,而非
SELECT *,尤其当表有大字段(如TEXT、BLOB)时,即使走索引也会因回表开销变大
ORDER BY 和 LIMIT 组合为何有时不走索引
MySQL 只有在排序字段和查询条件能被同一个索引“覆盖”时,才能避免额外排序和全表扫描。比如 WHERE status = 1 ORDER BY created_at LIMIT 10,如果只有 status 单列索引,MySQL 仍需取出所有 status = 1 的行再排序;但如果建了联合索引 (status, created_at),就能直接按索引顺序取前 10 条。
实操建议:
- 联合索引顺序很重要:等值查询字段放前面,范围/排序字段放后面,例如
WHERE a = 1 AND b > 10 ORDER BY c→ 建索引(a, b, c) -
LIMIT不会自动优化扫描行数,它只限制返回结果;若WHERE条件匹配 100 万行,LIMIT 10仍要先扫完符合条件的全部行(除非索引能跳过) - 避免
ORDER BY RAND(),它必然导致全表扫描 + 文件排序,应改用其他随机采样方式
哪些常见操作会让索引完全失效
不是加了索引就万事大吉。MySQL 的索引使用规则很具体,稍不注意就“形同虚设”。下面这些写法会让优化器直接放弃使用索引:
- 在索引列上使用
!=或(除主键外,通常走全表) -
WHERE中用OR连接多个条件,且其中任一字段无索引(整个条件可能退化为全表扫描) - 模糊查询以
%开头:LIKE '%abc'无法利用 B+ 树索引的有序性 - 对索引列使用
IS NULL或IS NOT NULL(部分版本和引擎下不走索引,尤其是非唯一索引) - 使用
NOT IN或NOT EXISTS(常导致索引失效,优先改写为LEFT JOIN ... IS NULL)
如何验证索引是否真的生效
别只看有没有建索引,要看 MySQL 实际执行时有没有用。最可靠的方式是结合 EXPLAIN 和实际执行统计。
实操建议:
- 执行
EXPLAIN FORMAT=JSON SELECT ...,关注key(实际使用的索引)、rows(预估扫描行数)、filtered(条件过滤率),比传统EXPLAIN更准 - 开启慢查询日志并设置
log_queries_not_using_indexes = ON,让 MySQL 主动记录未走索引的查询(注意生产环境慎开,有性能影响) - 用
SHOW INDEX FROM table_name确认索引结构,注意Seq_in_index表示联合索引中的字段顺序 - 对高频查询,可在测试库用
SELECT SLEEP(0.01)模拟并发压测,观察Handler_read_*状态变量变化(如Handler_read_next高说明走了索引扫描,Handler_read_rnd_next高说明大量回表或文件排序)
真正难的不是建索引,而是理解查询语义与索引结构之间的映射关系——同一张表,不同写法可能让优化器选择完全不同的执行路径,而这种差异往往藏在 EXPLAIN 的一行输出里。










