千万级数据表查询慢主因是执行计划选择全表扫描或低效索引;优化核心是重写扫描路径,包括用覆盖索引避免回表、高选择性条件前置、in 替代 or、force index+ 延迟关联等策略。

面对千万级数据表,查询慢往往不是因为 SQL 写得“错”,而是执行计划选了全表扫描或低效索引。真正有效的优化,常始于 扫描路径的重写——即主动引导优化器避开高成本访问方式,用更窄、更早过滤的路径替代宽泛扫描。
用覆盖索引切断回表,避免随机 IO
当 SELECT 字段和 WHERE 条件能被同一组索引完全覆盖时,数据库无需回主表查数据,扫描量直接从“全行 × 百万行”降到“索引键 × 百万行”。例如:
- 原查询:
SELECT user_id, name, email FROM users WHERE status = 1 AND created_at > '2023-01-01' - 低效:只有
(status)单列索引 → 扫描所有 status= 1 的记录,再逐条回表判断 created_at - 优化:建联合索引
(status, created_at, user_id, name, email)→ 索引内已含全部所需字段,走索引即可返回结果
把高选择性条件前置,压缩初始扫描集
联合索引的最左匹配原则决定了顺序很重要。应把区分度最高(如唯一 ID、状态码枚举值、时间范围精确到天)、能快速筛掉 90% 以上数据的列放在索引最左侧。
- 错误示例:
INDEX (user_type, created_at)—— 若 user_type 只有 ’admin’/’user’ 两类,前导列过滤能力极弱 - 推荐调整:
INDEX (created_at, user_type)—— 按日期范围先切出 1 天 / 1 周数据,再在小集合里分 type - 验证方法:用
EXPLAIN看rows是否明显下降,而非只盯type=range
用 IN 替代 OR,避免索引失效与松散扫描
多个独立等值条件用 OR 连接时,MySQL 可能放弃使用索引(尤其 5.7 以前),改走全表扫描;而 IN 列表可被优化器转为多个索引查找合并。
- 慎用:
WHERE category = 'A' OR category = 'B' OR category = 'C' - 替换为:
WHERE category IN ('A', 'B', 'C') - 注意:IN 值不宜过多(一般≤500),否则可能触发临时表或文件排序;超量时考虑分批或用临时表 JOIN
强制索引 + 延迟关联,绕过优化器误判
当统计信息滞后或数据分布倾斜严重时,优化器可能选错索引。此时可用 FORCE INDEX 锁定高效路径,并配合子查询先取主键再 JOIN,控制扫描基数。
- 典型场景:大表 JOIN 小表,但优化器错误选择小表驱动大表
- 写法示例:
SELECT t1.* FROM (SELECT id FROM orders FORCE INDEX (idx_status_time) WHERE status = 2 AND pay_time > '2024-01-01' LIMIT 1000) t2 JOIN orders t1 ON t1.id = t2.id; - 本质:用子查询明确限定“只扫 1000 行 ID”,再精准回查,避免全表扫描 + 嵌套循环






























