where col between x and y 慢主因是索引未被完全利用:范围条件截断后续字段的索引下推,导致回表或全索引扫描;需通过explain的key_len确认实际使用长度,并优化索引顺序与查询结构。

为什么 WHERE col BETWEEN x AND y 还是慢?
范围查询慢,往往不是因为没加索引,而是索引没被“完全用上”。MySQL 在 BETWEEN、>=、、<code>IN(非等值部分)这类操作中,只能利用索引的最左前缀直到第一个范围条件为止——后面字段即使有索引也失效。
比如表有联合索引 INDEX(a, b, c),执行 WHERE a = 1 AND b > 10 AND c = 5,实际只用到 a 和 b,c 不参与索引查找(会回表或全索引扫描),因为 b > 10 是范围条件,截断了后续字段的索引下推能力。
- 检查执行计划:务必看
EXPLAIN的key_len值,它告诉你实际用了索引前多少字节 - 避免在范围列后还依赖索引排序:如
ORDER BY a, b, c配合WHERE a = 1 AND b BETWEEN 10 AND 20,c无法走索引排序,会触发Using filesort - 若业务允许,把高选择性列前置:比如
b的取值范围远小于a,就别硬凑(a,b,c),考虑(b,a,c)或单独建(b)
IN 列表太长导致索引失效?
当 IN 包含几十甚至上百个值时,MySQL 可能放弃使用索引,转而走全表扫描——尤其在统计信息不准、或优化器预估成本过高时。这不是 bug,是代价权衡。
典型现象:WHERE id IN (1,2,3,...,200) 执行变慢,EXPLAIN 显示 type: ALL;但改成 id = 1 OR id = 2 OR ... 也不行,语法等价但优化器处理逻辑不同。
- 单列
IN且值少于 20–30 个时,通常能走索引;超过建议拆成临时表关联:SELECT * FROM t JOIN tmp_ids ON t.id = tmp_ids.id - 不要用
IN (SELECT ...)做大结果集子查询,改写为JOIN,否则容易触发DEPENDENT SUBQUERY,逐行执行 - 对字符串
IN,注意隐式类型转换:如WHERE status IN ('1','2','3')对应INT字段,会丢索引;统一用数字或加CAST
范围查询 + ORDER BY 怎么避免 Using filesort?
只要 ORDER BY 字段不在范围条件“覆盖范围内”,MySQL 就必须额外排序。哪怕你只查 10 行,也可能对百万行排序。
例如索引是 (a, b),语句是 WHERE a > 100 ORDER BY b —— a 是范围,b 虽在索引里,但索引中 b 的顺序已被 a 的范围打乱,无法直接按 b 有序读出。
- 如果必须按
b排序,且a是范围,优先建索引(b, a)(前提是b选择性够) - 复合条件如
WHERE a = 1 AND b > 10 ORDER BY c,想免排序就得索引包含(a, b, c),且a是等值、b是范围、c是排序字段——这时c才能被索引有序利用 - 用
LIMIT不能阻止排序发生,只是减少输出量;真正省排序,得靠索引结构匹配
日期范围查询为什么有时走索引,有时不走?
DATE、DATETIME 字段上的范围查询极易因函数误用丢失索引,比如 WHERE YEAR(create_time) = 2023 或 WHERE DATE(create_time) = '2023-01-01' —— 这些都会让索引失效,因为字段被函数包裹,无法直接比较索引值。
另一个坑是时区和隐式转换:create_time 是 DATETIME,但传入字符串 '2023-01-01',MySQL 可能补零变成 '2023-01-01 00:00:00',看起来没问题,但若字段是 TIMESTAMP 且数据库时区不同,可能引发意外转换。
- 写范围别用函数:用
WHERE create_time >= '2023-01-01' AND create_time ,而不是 <code>YEAR()或DATE() - 确认字段类型和查询值类型一致:
DATETIME字段就用'2023-01-01 00:00:00',别混用DATE字面量 - 测试时用
SHOW WARNINGS查看是否触发隐式转换,常见提示如Warning | 1292 | Truncated incorrect datetime value
范围查询优化的核心,从来不是“加个索引就完事”,而是看清索引如何被截断、排序如何被绕过、函数如何悄悄吃掉你的性能。这些地方一错,加再多索引也没用。











