千万级数据表查询慢主因是执行计划选择全表扫描或低效索引;优化核心是重写扫描路径,包括用覆盖索引避免回表、高选择性条件前置、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”,再精准回查,避免全表扫描+嵌套循环










