一张订单表涨到两千多万行,按 RANGE 按月分区,改完一测,慢查询还是那个耗时,几乎没动。有人以为是分区没建成功,其实分区本来就不是用来提速的。
先看裁剪有没有命中
EXPLAIN 的输出里有一列叫 partitions。查某个月的数据,这一列正常应该只显示对应的那一两个分区;如果 p0 到 p23 全列出来,说明裁剪没生效,语句照样扫了全部分区,还多了一层管理开销。
裁剪的条件是 WHERE 里直接出现分区键,而且没有被函数包住。写成 DATE_FORMAT(created_at,单引号 %Y-%m 单引号) 这种,优化器认不出来,只能把所有分区打开。改成时间区间比较,从某日零点到某日零点,裁剪立刻生效。分区键上做隐式类型转换也会让裁剪失效,字符串列传数字进去,照样全开。
二级索引是跟着分区走的
InnoDB 的每个分区在物理上就是一张独立的表,二级索引也一样。查询条件不带分区键时,走二级索引拿到的主键只属于某个分区,跨分区就得每个分区各查一遍再合并。分区越多,这一步越慢,这也是很多人分区之后反而更慢的原因。
几个绕不开的限制
- 唯一索引必须包含分区列,主键同理,不满足就得改表结构
- 分区表不支持外键,原有关联约束要拆掉
- 分区数量太多,打开表和元数据的开销会上来
- 分区方式定下来之后,再改代价很大,通常要重建
它真正擅长的事
按时间分区最大的好处是删历史数据。DROP PARTITION 是秒级操作,直接丢文件;DELETE 几百万行要写 undo、要刷日志,还长时间占锁。归档、冷热分离、按分区做备份恢复,这些才是分区的主场。
还有个容易忽略的点:每个分区各自维护统计信息,跨分区查询时优化器靠估算挑分区,估偏了执行计划就很怪。定期 ANALYZE TABLE 是必要动作,不然分区越多,计划越飘。
该先做什么
慢查询先看执行计划和索引,缺索引就补,回表多就改覆盖索引。数据量真到单表撑不住的规模,再考虑分区或者分库分表。顺序搞反,分区做完发现没用,回头还得维护一堆分区,纯给自己加活。