分区裁剪未触发导致JOIN全表扫描,根本原因是WHERE未显式约束时间范围、JOIN条件缺失分区键、索引顺序不匹配扫描模式。

加了分区和索引,JOIN 还是全表扫?不是分区没用,而是「分区裁剪」根本没触发——关键在 WHERE 条件是否显式约束时间范围、JOIN 条件是否含分区键、索引是否匹配扫描顺序。
为什么 EXPLAIN 显示 type: ALL 或 rows 高得离谱
常见现象:建了 PARTITION BY RANGE (event_time),也加了 INDEX (event_time),但 JOIN 一跑就扫全分区。根本原因不是索引没建,而是优化器压根没识别出可裁剪的边界。
- JOIN 条件里没出现分区键,比如只写
o.user_id = p.user_id,却漏掉o.event_time = p.event_time或时间重叠表达式 - WHERE 中没写能触发裁剪的时间范围,例如缺了
WHERE o.event_time >= '2026-05-01'这类硬约束 - 分区键字段类型不一致:左表是
DATETIME,右表是TIMESTAMP,隐式转换让分区裁剪和索引同时失效 - 在分区键上用了函数,如
DATE(o.event_time)或o.event_time + INTERVAL 1 HOUR,直接禁用裁剪能力
WHERE 必须带明确时间范围才能激活分区裁剪
分区裁剪不是自动生效的,它依赖优化器从 WHERE 子句中推导出可排除的分区。没有这个前提,分区只是徒增管理负担。
- ✅ 正确写法:
WHERE o.event_time >= '2026-05-01' AND o.event_time (开区间更安全,避免边界精度问题) - ✅ 支持裁剪的组合:
WHERE o.event_time BETWEEN '2026-05-01' AND '2026-05-31 23:59:59'(注意BETWEEN是闭区间,需确认字段精度) - ❌ 无效写法:
WHERE DATE(o.event_time) = '2026-05-10'(函数导致裁剪和索引双失效) - ❌ 无效写法:
WHERE o.event_time + INTERVAL 1 DAY = '2026-05-11'(表达式破坏索引定位能力)
JOIN 条件中必须包含分区键的等值或区间匹配
仅靠 WHERE 裁剪左表还不够;右表(被驱动表)要走 Range Scan,JOIN 条件本身就得提供时间维度的收敛能力。
- 若右表按
valid_from分区,JOIN 条件应含类似:o.event_time BETWEEN p.valid_from AND p.valid_until - 若右表有
INDEX (valid_from, valid_until),且分区键是valid_from,这个条件才能让优化器在单一分区内做索引范围扫描 - 避免写成:
o.event_time >= p.valid_from - INTERVAL 1 HOUR—— 表达式会让右表索引无法用于范围定位 - MySQL 8.0+ / PostgreSQL 可考虑改用
LATERAL或ROW_NUMBER()替代区间 JOIN,尤其当只需取“每个事件匹配的最新生效记录”时
索引顺序必须匹配查询模式,否则白建
时间字段在索引中的位置,决定它能不能支撑范围扫描。放错位置,等于没建。
- 纯时间范围查询:
INDEX (event_time)即可,不必强求复合索引 - 高频带租户 ID 的查询:
INDEX (tenant_id, event_time)—— 等值字段在前,范围字段在后 - ❌ 错误组合:
INDEX (event_time, tenant_id),当只查WHERE event_time >= ...时,tenant_id完全用不上 - 若还需过滤状态字段,扩展为:
INDEX (tenant_id, event_time, status),可覆盖更多场景
最容易被忽略的是:分区裁剪和索引范围扫描从来不是孤立生效的,它们必须在同一个执行路径里协同工作——WHERE 提供分区边界,JOIN 提供右表收敛依据,索引结构匹配扫描方向。少一个环节,性能就回落到全量扫描水平。










