为什么 SELECT *
在大表上会突然变慢
不是因为查询本身复杂,而是数据库被迫做全表扫描。没有合适索引时,MySQL 必须逐行读取磁盘页,I/O 成为瓶颈。尤其当
WHERE条件字段没建索引、或用了函数/表达式(如
WHERE YEAR(created_at) = 2023),索引就失效了。 避免在索引列上使用函数、类型转换或运算,例如
WHERE price + 10 > 100会让
price索引失效
SELECT *在宽表中还可能触发临时表和文件排序,用
EXPLAIN查看
Extra字段是否出现
Using filesort或
Using temporary联合索引要注意最左前缀原则:对
(a, b, c)建索引,
WHERE b = 1 AND c = 2无法命中,但
WHERE a = 1 AND b = 2可以
EXPLAIN
输出里哪些字段真正影响性能判断
重点关注
type、
key、
rows和
Extra四个字段,它们直接反映执行路径质量。
type值从好到差:
const≈
eq_ref>
ref>
range>
index>
ALL;出现
ALL基本等于全表扫描
key为空说明没走索引;若非空但
rows值远大于实际结果数,可能是索引选择性差(比如对性别字段建索引)
Extra中出现
Using index condition是好的(ICP 下推),但
Using where; Using index表示覆盖索引,而
Using filesort就得警惕:ORDER BY 没走索引或顺序不匹配
什么时候该建联合索引而不是单列索引
当多个字段经常同时出现在
WHERE、
ORDER BY或
GROUP BY中,且顺序稳定时,联合索引能显著减少索引数量和维护开销。 例如高频查询是
SELECT name FROM user WHERE status = ? AND city = ? ORDER BY created_at DESC,优先建
(status, city, created_at)联合索引,而非三个单列索引 把等值条件字段放前面,范围/排序字段放后面:
(a = ?, b > ?, c ORDER BY)→ 索引应为
(a, b, c),不是
(a, c, b)注意区分「覆盖索引」需求:如果只查索引字段(如
SELECT status, city FROM user WHERE ...),联合索引还能避免回表
索引不是越多越好:写入变慢和空间膨胀怎么权衡
每个索引都是 B+ 树,INSERT/UPDATE/DELETE 都要同步更新所有相关索引。业务高峰期大量写入时,过多索引会明显拖慢响应,且占用更多磁盘与内存。
单表索引数建议控制在 5–7 个以内;可通过information_schema.STATISTICS查看各索引的
INDEX_COMMENT和使用频率(需开启
userstat或用
performance_schema) 删除长期未被使用的索引:比如某索引在
sys.schema_unused_indexes视图中持续多日无记录 TEXT/BLOB 字段不能直接建索引,要用前缀长度(如
content(100)),但前缀过短会导致重复率高、选择性差
SHOW INDEX FROM orders\G -- 查看索引字段顺序、是否唯一、基数(Cardinality)等,基数越接近行数,索引区分度越高
索引优化本质是让 MySQL 少读磁盘、少算中间结果。很多“慢查询”背后不是 SQL 写得差,而是数据分布变了、统计信息没更新、或者上线后业务查询模式偏移了——这些细节比“加个索引”更难察觉。
