WHERE col BETWEEN a AND b 比 col >= a AND col
为什么 WHERE col BETWEEN a AND b 有时比 WHERE col >= a AND col 慢?
二者语义等价,但 MySQL 优化器对 BETWEEN 的常量推导更保守,尤其当涉及函数或隐式类型转换时。例如 created_at BETWEEN '2024-01-01' AND NOW() 中 NOW() 是非确定性函数,可能导致索引失效;而显式写成 created_at >= '2024-01-01' AND created_at 反而更容易被提前物化。
- 优先用
>=和替代BETWEEN,尤其右侧边界含函数、子查询或变量 - 确保两端值类型一致:比如
id BETWEEN '1' AND '100'会触发字符串比较,若id是INT,应写成id BETWEEN 1 AND 100 - 避免在字段上套函数:
DATE(created_at) BETWEEN ...必然无法走索引,改用created_at >= '2024-01-01' AND created_at
复合索引中范围条件必须放最后吗?
是的,且这是最容易踩的坑。MySQL 的 B+ 树索引只能高效支持「最左前缀 + 等值匹配 + 最多一个范围匹配」的组合。一旦出现范围(>、、BETWEEN、LIKE 'abc%'),其右侧所有字段都无法用于索引查找。
- 对于查询
WHERE status = 1 AND created_at > '2024-01-01' AND user_id = 123,索引(status, created_at, user_id)只能用到前两个字段,user_id不参与索引查找 - 如果业务中
user_id过滤性更强,应调整为(status, user_id, created_at),把范围字段放在最后 -
IN列表不算严格意义上的“范围”,但 MySQL 8.0+ 对IN后多个常量会做等值展开,仍可继续使用后续字段 —— 但别依赖这个行为,IN超过几百项时性能会断崖下跌
如何判断范围查询是否真的走了索引?
光看 EXPLAIN 的 type 是 range 不够,得结合 key_len、rows 和实际执行时间验证。常见假象是:索引存在、type 显示 range,但 key_len 远小于预期,说明只用了索引前缀。