BETWEEN AND未走索引主因是优化器预估扫描行数超20%–30%阈值而弃用索引,或违反最左前缀原则、存在隐式类型转换、范围字段非复合索引末位等。

BETWEEN AND 查询为什么没走索引?
不是所有 BETWEEN AND 都能用上索引——它只在满足 B+ 树最左前缀原则、且字段类型匹配时才生效。常见失效场景包括:
- 查询条件跳过复合索引的左侧列,比如索引是
(user_id, create_time),但只写WHERE create_time BETWEEN '2023-01-01' AND '2023-01-31' - 对字段做了隐式类型转换,比如
create_time是DATETIME,但传入字符串'2023-01-01'时 MySQL 自动加了时区或截断,导致无法命中索引 - 范围过大(如查一年数据),优化器预估扫描行数超过全表 20%–30%,主动放弃索引走全表扫描
复合索引中范围字段必须放最后
MySQL 的 B+ 树索引在遇到第一个范围条件(BETWEEN、>、<、LIKE 'abc%')后,后续字段就不再用于索引查找了。这是“范围查询截断”现象。
例如索引 (status, create_time, user_id):
-
WHERE status = 'paid' AND create_time BETWEEN '2023-01-01' AND '2023-01-31'→ 只用到前两列,user_id不参与索引查找 -
WHERE status = 'paid' AND user_id = 123 AND create_time BETWEEN ...→user_id在范围字段之后,完全失效
正确做法是把等值条件放前面、排序字段居中、范围字段放最后:比如高频查 status = ? ORDER BY create_time DESC LIMIT 20,就建 (status, create_time);若还要按 user_id 过滤,且是等值,则放 status 后、create_time 前:(status, user_id, create_time)。
EXPLAIN 中 type=range 是正常,但 key_len 要看全
当 EXPLAIN 显示 type = range,说明索引已启用,但不等于高效。关键要看 key_len 是否达到预期长度:
- 假设索引是
(a, b, c),其中a是INT(4 字节),b是VARCHAR(50)(utf8mb4 下最多 200 字节),c是TINYINT(1 字节) -
WHERE a = 1 AND b LIKE 'x%' AND c BETWEEN 1 AND 5→key_len应为4 + 实际匹配的 b 长度 + 1;如果只显示4,说明b和c没被用上 -
Extra出现Using filesort或Using temporary,往往是因为范围字段后还有ORDER BY或GROUP BY无法复用索引顺序
日期范围查询要警惕分区和时区陷阱
用 BETWEEN 查日期时,容易忽略存储格式与查询逻辑的错位:
-
order_date是DATETIME类型,但BETWEEN '2023-01-01' AND '2023-01-31'实际查的是2023-01-01 00:00:00到2023-01-31 00:00:00,漏掉 31 日全天数据 - 更安全写法是:
order_date >= '2023-01-01' AND order_date < '2023-02-01' - 如果表启用了按月分区(
PARTITION BY RANGE (TO_DAYS(order_date))),确保查询条件能精准命中分区,否则会扫描所有分区 - 应用层传入的时间戳未转成数据库所在时区,也可能导致索引失效(如 PHP 默认用 UTC,MySQL 配置为 CST)
真正卡住性能的,往往不是要不要建索引,而是建在哪一列、怎么写 WHERE、以及有没有意识到 BETWEEN 在索引结构里的“断点”位置。



















