mysql数据库中的查询执行效率与索引优化

SELECT *在大表上变慢主因是全表扫描,因缺失索引或索引失效(如函数、运算、类型转换),导致I/O瓶颈;宽表还易触发临时表和文件排序。

为什么 SELECT * 在大表上会突然变慢

不是因为查询本身复杂,而是数据库被迫做全表扫描。没有合适索引时,MySQL 必须逐行读取磁盘页,I/O 成为瓶颈。尤其当 WHERE 条件字段没建索引、或用了函数/表达式(如 WHERE YEAR(created_at) = 2025),索引就失效了。

  • 避免在索引列上使用函数、类型转换或运算,例如 WHERE price + 10 > 100 会让 price 索引失效
  • SELECT * 在宽表中还可能触发临时表和文件排序,用 EXPLAIN 查看 Extra 字段是否出现 Using filesortUsing temporary
  • 联合索引要注意最左前缀原则:对 (a, b, c) 建索引,WHERE b = 1 AND c = 2 无法命中,但 WHERE a = 1 AND b = 2 可以

EXPLAIN 输出里哪些字段真正影响性能判断

重点关注 typekeyrowsExtra 四个字段,它们直接反映执行路径质量。

  • type 值从好到差: consteq_ref > ref > range > index > ALL;出现 ALL 基本等于全表扫描
  • key 为空说明没走索引;若非空但 rows 值远大于实际结果数,可能是索引选择性差(比如对性别字段建索引)
  • Extra 中出现 Using index condition 是好的(ICP 下推),但 Using where; Using index 表示覆盖索引,而 Using filesort 就得警惕:ORDER BY 没走索引或顺序不匹配

什么时候该建联合索引而不是单列索引

当多个字段经常同时出现在 WHEREORDER BYGROUP 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 写得差,而是数据分布变了、统计信息没更新、或者上线后业务查询模式偏移了——这些细节比“加个索引”更难察觉。