mysql如何创建合适的索引_mysql设计优化方法

2次阅读

无索引时where字段查询会触发全表扫描,导致性能下降;应通过explain检查执行计划,为高频过滤字段建单列或按高过滤性→等值→范围顺序建联合索引,并避免在索引列上使用函数。

mysql如何创建合适的索引_mysql设计优化方法

WHERE 条件字段没索引,查询直接变慢

mysql 不会自动给 WHERE 中的字段建索引,哪怕你频繁用 user_idstatus 过滤。没索引时,每次查询都可能触发全表扫描,尤其当表超过 10 万行,响应时间就明显抖动。

实操建议:

  • EXPLAIN select ...type 是否为 ALL(全表扫描),key 列是否为 NULL
  • 单列查询优先建单列索引:CREATE INDEX idx_user_id ON orders(user_id);
  • 多条件组合查询(如 WHERE category = ? AND status = ? AND created_at > ?),按「过滤性高 → 等值 → 范围」顺序建联合索引,例如:CREATE INDEX idx_cat_status_time ON orders(category, status, created_at);
  • 避免在索引列上做函数操作,比如 WHERE YEAR(created_at) = 2024 会让索引失效;改用范围写法:created_at >= '2024-01-01' AND created_at

ORDER BY 和 GROUP BY 没走索引,临时表+文件排序很伤

ORDER BY 字段不在索引覆盖范围内,MySQL 可能创建内部临时表并进行 filesort,内存不够时还会写磁盘,性能断崖式下跌。

实操建议:

  • 联合索引天然支持最左前缀的排序,比如已有 INDEX idx_a_b_c (a,b,c),那么 ORDER BY a,bORDER BY a,b,c 可走索引;但 ORDER BY b,c 就不行
  • GROUP BY 同理,尽量让分组字段匹配索引最左部分;若还需聚合后排序,把排序字段也纳入索引尾部,例如:GROUP BY user_id ORDER BY created_at DESC → 建 INDEX idx_user_created (user_id, created_at)
  • 确认是否用了 using filesortUsing temporary:执行 EXPLAIN format=TRADITIONAL 查看 Extra

索引太多或太宽,写入和维护成本反超收益

每加一个索引,INSERT/UPDATE/delete 都要同步更新 B+ 树,索引列越多、越长(比如 VARCHAR(500)),B+ 树层级越深、页分裂越频繁,反而拖慢写操作。

实操建议:

  • 单个索引字段数别超 3 个,字符串字段优先用前缀索引:INDEX idx_title_prefix (title(10))(前提是前 10 字节足够区分)
  • 删除长期不用的索引:查 information_schema.STATISTICS 或用 sys.schema_unused_indexes(MySQL 8.0+)
  • 区分「高频读 + 低频写」和「高频写」表——后者索引要更克制,宁可加缓存也不盲目加索引
  • 注意隐式类型转换导致索引失效,比如 user_idBIGINT,但 SQL 中传了字符串 '123',MySQL 可能放弃索引做全表转换

覆盖索引没利用好,回表成性能瓶颈

当查询字段都在索引中(比如 SELECT user_id, status FROM orders WHERE user_id = 123),MySQL 可直接从索引 B+ 树叶子节点取值,无需回主键索引查整行——这就是覆盖索引。否则要先查索引定位主键,再用主键查聚簇索引,I/O 翻倍。

实操建议:

  • 对高频查询语句,把 SELECT 中的字段尽可能塞进联合索引末尾,例如常用 SELECT id, user_id, status, amount,WHERE 用 user_id,可建:INDEX idx_user_cover (user_id, id, status, amount)
  • 注意主键自动包含在二级索引中,所以 SELECT user_id, idINDEX idx_user (user_id) 上已是覆盖查询
  • 别为了覆盖而覆盖:加太多冗余字段会让索引体积暴涨,评估时对比 data_lengthindex_length(查 SHOW table STATUS

索引不是越多越好,也不是建了就生效;真正关键的是理解查询模式、看清执行计划、持续验证效果。很多慢查问题其实卡在「以为走了索引,实际没走」或者「走了索引但回表太重」,而不是索引本身的设计。

text=ZqhQzanResources