mysql范围查询如何优化_mysql查询性能提升方法

6次阅读

WHERE col BETWEEN a AND b 比 col >= a AND col

mysql范围查询如何优化_mysql查询性能提升方法

为什么 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' 会触发字符串比较,若 idint,应写成 id BETWEEN 1 AND 100
  • 避免在字段上套函数:DATE(created_at) BETWEEN ... 必然无法走索引,改用 created_at >= '2024-01-01' AND created_at

复合索引中范围条件必须放最后吗?

是的,且这是最容易踩的坑。MySQL 的 B+ 树索引只能高效支持「最左前缀 + 等值匹配 + 最多一个范围匹配」的组合。一旦出现范围(>BETWEENLIKE '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 超过几百项时性能会断崖下跌

如何判断范围查询是否真的走了索引?

光看 EXPLaiNtyperange 不够,得结合 key_lenrows 和实际执行时间验证。常见假象是:索引存在、type 显示 range,但 key_len 远小于预期,说明只用了索引前缀。

  • 执行 EXPLAIN format=TREE(MySQL 8.0+)看索引扫描路径,比传统 EXPLAIN 更直观
  • 检查 key_len:比如索引是 (a INT, b VARCHAR(50)),若 aNULL,单列 INT 占 5 字节(4+1),b 若为 utf8mb4 字符集且定义长度 50,则最多占 200 字节(50×4),加起来 205;若实际 key_len=5,说明 b 完全没用上
  • select * FROM t WHERE ... INTO DUMPFILE '/tmp/test' 配合慢日志中的 Rows_examined 对比,确认是否真按索引行数扫描

时间范围查询分页深翻怎么不崩?

LIMIT offset, size 做深分页时,即使有索引,MySQL 仍要扫描 offset + size 行才能跳过前面的数据 —— offset 越大越慢。时间范围本身不能解决这个问题,但可以换思路。

  • 放弃 OFFSET,改用游标分页:记录上一页最后一条的 created_atid,下一页查 WHERE created_at >= ? AND id > ? ORDER BY created_at, id LIMIT 20
  • 如果必须用时间范围分页,先用覆盖索引查出 ID 列表(如 SELECT id FROM t WHERE time BETWEEN ... ORDER BY time LIMIT 20000, 20),再用 IN 回表,比直接 LIMIT 20000,20 快得多
  • 对实时性要求不高的场景,考虑预生成时间分区表(如按天/月分表),查某天数据时直接路由到对应表,避免大范围扫描

索引设计不是一锤子买卖,范围条件的位置、数据分布倾斜度、查询并发模式都会影响最终效果。上线前务必用生产级数据量压测,而不是只看 EXPLAIN 的理想路径。

text=ZqhQzanResources