分区表需为每个分区单独建索引才能避免全分区扫描;分区键(如create_time)仅用于分区剪枝,高频查询字段(如device_id)须建独立或复合索引;推荐idx_device_time及覆盖索引;分区数不宜超64个,建议按月分区;定期ANALYZE TABLE并用EXPLAIN验证。

分区表本身不自动带来索引优化效果,必须在每个分区内部单独建立有效索引,否则查询仍会全分区扫描。
分区字段和索引字段必须分开设计
很多人误以为按 create_time 分区后,就天然能加速 WHERE create_time BETWEEN ... 查询——其实不然。MySQL 的分区剪枝(partition pruning)只决定「访问哪些分区」,但每个被选中的分区内部仍需靠索引定位数据。如果分区内部没建索引,就会退化为该分区内的全表扫描。
- 正确做法:分区键(如
create_time)用于控制数据分布,而高频查询条件列(如device_id、user_id)必须作为独立索引或复合索引的前导列 - 典型错误:只建
INDEX(create_time),却忽略轨迹类查询最常带device_id = ?条件 - 推荐组合:
CREATE INDEX idx_device_time ON trajectory (device_id, create_time)—— 这样既能支撑WHERE device_id = ? AND create_time > ?,又符合最左前缀原则
RANGE 分区 + 覆盖索引可避免回表
轨迹数据通常只需查时间范围内的 lat/lng/speed 等字段,不需要整行。这时在分区表上构建覆盖索引,能跳过聚簇索引回表,显著减少 I/O。
- 例如:
CREATE INDEX idx_device_time_pos ON trajectory (device_id, create_time) INCLUDE (lat, lng, speed)(MySQL 8.0.13+ 支持INCLUDE;旧版本需写成(device_id, create_time, lat, lng, speed)) - 验证是否生效:用
EXPLAIN查看Extra是否含Using index,且rows明显低于总数据量 - 注意:
INCLUDE列不参与排序和过滤,仅用于覆盖;若查询含ORDER BY lat,则lat必须进索引前缀
分区数量过多反而拖慢查询优化器
MySQL 5.7+ 对分区数超过 64 个时,优化器成本估算可能失真,导致本该走 range 扫描的查询误选 ALL 或 ref 类型,尤其当 WHERE 条件未精确匹配分区键时。
- 轨迹表按天分区?一年就是 365 个分区——已超安全阈值。建议改用按月或按双月(如
p202501_02) - 用
SELECT PARTITION_NAME FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'trajectory'定期检查分区总数 - 删除过期分区用
ALTER TABLE trajectory DROP PARTITION p202401,比DELETE快几个数量级,且不锁全表
真正卡住性能的往往不是分区逻辑本身,而是忘了每个分区仍是“一张表”——它需要自己的索引策略、自己的统计信息更新(ANALYZE TABLE trajectory)、以及和业务查询模式严丝合缝的字段顺序。一个没被 EXPLAIN 验证过的分区+索引组合,和没建索引没区别。


















