分区表的使用与边界
NeonBloom
6 min read
判断先说:分区表是把一张逻辑大表在物理上按规则切成多个片段,查询时能"只扫相关分区"来提速、运维时能"直接 Drop 旧分区"来清理数据。但它不是银弹——分区键选错、跨分区查询,性能反而更糟,且对唯一索引有额外约束。用之前先想清楚访问模式,比盲目分区重要。
分区的本质
对应用层而言,分区表还是一张表,SQL 写法不变。底层按分区键把数据路由到不同物理文件。优化器在 WHERE 带了分区键时,能做"分区裁剪",只访问命中的分区,跳过大片无关数据。
-- 按时间范围分区
CREATE TABLE orders (
id BIGINT,
user_id BIGINT,
create_time DATETIME
) PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),
PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
分区键的选择决定了裁剪能否生效。选得好,大多数查询只落在一两个分区;选得不好,查询依然要扫全部分区,甚至更慢。这是分区设计里最关键的一步决策。
什么时候分区真正有用
按时间清理冷数据。日志、订单流水这类"只保留最近 N 个月"的表,用 DROP PARTITION 直接删整块,比 DELETE ... WHERE create_time < xxx 快几个数量级,且不产生大量 UNDO、不碎表、不长时间锁表。DELETE 大批量数据会写满日志、拖慢主库,而 DROP PARTITION 是元数据操作,几乎瞬时完成。
热点数据天然隔离。按某个离散度高的键(如租户 id)分区,让不同租户的写分散到不同分区,减少单点锁竞争。多租户 SaaS 系统常用这招把不同客户的负载在物理上隔开。
超大表的查询裁剪。查询条件总能带上分区键时,扫描范围被切成几分之一,I/O 明显下降。例如按月份分区后,查"本月订单"只扫一个分区,而不是整张历史表。
边界与坑
分区键必须包含在唯一约束里。想建 UNIQUE(user_id),但表按 create_time 分区,就必须在唯一索引里加上分区键,否则无法保证跨分区的全局唯一。这常常逼着你调整索引设计,甚至改变业务唯一性的表达方式。
-- 分区表上, 唯一索引必须含分区键
ALTER TABLE orders ADD UNIQUE KEY uk_user_time (user_id, create_time);
查询不带分区键会扫全部分区。分区裁剪失效后,等于同时查了 N 张小表再合并,开销可能比单表还大,因为多了合并和路由成本。如果业务查询大多不带分区键,分区带来的好处就很有限,甚至为负。设计前务必统计真实查询模式。
分区数量不是越多越好。上百个分区会让元数据膨胀、优化器规划变慢,单次查询的"打开分区"成本累积明显。经验上,按时间分区保持"当前加近期加少量未来预建"即可,不要提前建几百个。预建太多既占元数据又增加维护心智负担。
跨分区聚合代价高。涉及多分区的 GROUP BY、排序,需要在各分区结果上再做归并,内存和临时表压力大。COUNT(*) 全表统计在分区表上不一定比单表快,因为要汇总各分区。
分区与分库分表的取舍
分区表仍在单实例内,解决不了单实例容量和写入上限的瓶颈。当数据量和写入已经压到单机极限,要上的是分库分表或迁移到分布式数据库,而不是继续堆分区。分区适合"单机、表很大、访问有清晰维度"的场景,是运维友好型优化,不是水平扩展方案。把它用在恰当的场景能省很多事,用错场景则徒增复杂度。
分区表与分表的区别别混淆
分区表和手动分表(把数据按规则拆成多张真实表)容易混淆,但差别很大。分区表对应用透明,SQL 不变,路由和合并由数据库做;手动分表要应用自己决定往哪张表读写,跨表查询要应用层聚合。分区表的优势是透明和运维方便,劣势是仍在单实例、且受数据库分区实现限制;手动分表能跨实例水平扩展,但开发和维护成本高。选型时先问"问题到底是单表太大还是单机扛不住":前者分区表够用,后者必须分库分表。用分区表去解扩展性问题,只会发现瓶颈依旧在单机,问题并没有被真正解决。
分区键选错的代价
分区键一旦定下,后期想换代价很大,往往要重建整张表、迁移数据。所以设计时要基于"未来一年主要的查询维度"来选,而不是当前恰好方便。曾有系统按天分区后,业务查询变成按用户维度,分区裁剪完全失效,反而比不分还慢。选错比不选更糟,因为没人会怀疑"已经分区了"的表还有性能问题,排查起来更绕。分区键决策值得在前期多花时间,它是整个分区设计里最不可逆的一环。
1 view
0 likes
0 comments