mysql如何避免全表扫描_mysql查询优化思路

1次阅读

explain显示type=all需警惕,因mysql正全表扫描,数据量大时响应慢;主因是索引未命中最左前缀、函数或隐式类型转换致失效;应检查key是否NULL、rows是否接近总行数,并优化查询条件与索引设计。

mysql如何避免全表扫描_mysql查询优化思路

为什么 EXPLAIN 显示 type=ALL 就该警惕

这代表 MySQL 正在对整张表逐行扫描,尤其在数据量上万后,响应会明显变慢。常见诱因不是没加索引,而是查询条件没命中索引最左前缀、用了函数或隐式类型转换,导致索引失效。

实操建议:

  • EXPLAIN select ...key 列是否为 NULLrows 是否接近表总行数
  • 检查 WHERE 中字段是否在复合索引的最左侧位置,比如索引是 (a, b, c),但查询只用了 b = ?,那就不会走索引
  • 避免在索引列上做运算:WHERE YEAR(create_time) = 2024 → 改成 WHERE create_time >= '2024-01-01' AND create_time
  • 字符串字段用数字查询会触发隐式转换:WHERE mobile = 13812345678(mobile 是 VARCHAR)→ 改成 WHERE mobile = '13812345678'

哪些 ORDER BY 会绕过索引排序

即使 WHERE 走了索引,如果 ORDER BY 字段不在索引中,或顺序/方向不一致,MySQL 仍可能触发 using filesort,严重时连带放弃索引范围扫描。

实操建议:

  • 复合索引 (a, b, c) 可支持 ORDER BY a, bORDER BY a DESC, b ASC(注意:MySQL 8.0+ 才支持混合方向;5.7 只支持全 ASC 或全 DESC)
  • ORDER BY a, c 无法利用 (a, b, c) 索引的排序能力,因为跳过了 b,中间断层
  • 若业务允许,把 SELECT * 改成只查必要字段,减少排序开销;更彻底的做法是加覆盖索引,例如 INDEX (a, b, status, created_at) 来支撑 WHERE a=? AND b=? ORDER BY created_at DESC

LIMIT 加得再早,也救不了没索引的 OFFSET

SELECT * FROM t ORDER BY id LIMIT 10000, 20 看似只取 20 行,但 MySQL 仍要先定位到第 10001 行——这意味着它得扫描前 10020 行。偏移越大,越接近全表扫描。

实操建议:

  • 用游标替代分页:记录上一页最后的 id,下一页查 WHERE id > 12345 ORDER BY id LIMIT 20
  • 如必须用 OFFSET,确保 ORDER BY 字段有索引,且该索引能同时满足 WHERE 条件(否则先排序再过滤,代价更高)
  • 对大表分页,可先用子查询缩小主表扫描范围:SELECT * FROM t JOIN (SELECT id FROM t WHERE status=1 ORDER BY id LIMIT 10000, 20) AS tmp USING (id)

统计类查询为什么总扫全表

count(*) 在 InnoDB 中不直接存总行数,但也不是每次都全扫——关键看有没有可用的二级索引。如果没有,就只能走聚簇索引(也就是整张表);如果有非空索引(比如 INDEX(status)),优化器可能选它来遍历,更快。

实操建议:

  • 想加速 COUNT(*),建一个不为空的窄索引,例如 INDEX(dummy),其中 dummy 是定义为 NOT NULL 的小字段(如 TINYINT
  • 避免用 COUNT(column) 统计非空行,除非真需要判空;COUNT(*)COUNT(1) 在语义和性能上等价
  • 实时精确总数本身反模式,高并发场景建议用缓存 + 增量更新,或采样估算(如 SELECT TABLE_ROWS FROM information_schema.TABLES,但它是估算值)

真正难处理的是那些“看起来能走索引、实际却失效”的组合:函数、OR 条件、!=NOT INjson 字段查询……这些地方最容易漏掉执行计划验证。

text=ZqhQzanResources