MySQL、PostgreSQL、ClickHouse 的分区均不支持可视化添加,必须手写SQL;加分区需注意主键包含分区字段、子表约束匹配、分区键为ORDER BY前缀等规则,且已有数据时性能影响大。
MySQL 8.0+ 添加 RANGE/LIST 分区必须用 ALTER TABLE ... PARTITION BY
分区不能“可视化添加”——没有图形界面能绕过 sql 定义分区逻辑。mysql workbench、dbeaver 等工具的“表结构编辑器”里点来点去,最终保存时仍会生成并执行 alter table 语句,且不支持交互式分区分裂或迁移。想加分区,就得手写分区表达式。
常见错误现象:ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function —— 这是因为你用了主键但没把分区字段包含进主键里。
- 使用场景:按时间(
created_at)或状态码(status)做归档或查询隔离 - 必须先确认字段有合适的数据类型和索引:RANGE 要数值或日期,LIST 要离散值,HASH 只支持单列且必须是整型表达式
- 已有数据的表加分区,性能影响极大:MySQL 会重建整个表,锁表时间与数据量正相关
- 示例(按月 RANGE 分区):
ALTER TABLE orders PARTITION BY RANGE (YEAR(created_at)*100 + MONTH(created_at)) ( PARTITION p202301 VALUES LESS THAN (202302), PARTITION p202302 VALUES LESS THAN (202303), PARTITION p202303 VALUES LESS THAN (202304) );
PostgreSQL 的 CREATE TABLE ... PARTITION BY 不支持对现有表在线分区
PostgreSQL 10+ 的声明式分区是“只读继承”的增强版,但 ALTER TABLE ... ATTACH PARTITION 要求目标分区表已存在、结构完全一致、且无数据冲突。不存在“向导式一键分区”——你得自己建子表、加约束、再挂载。
容易踩的坑:ERROR: partition constraint is violated by some row,说明待挂载的子表里有数据不满足父表的分区键约束。
- 使用场景:按地区(
region_code)、租户 ID(tenant_id)做逻辑隔离 - LIST 分区要求子表
CHECK约束必须和父表PARTITION BY LIST表达式严格匹配,多一个值、少一个值都不行 - 性能影响:分区裁剪(partition pruning)依赖查询条件是否含分区键,
WHERE status = 'done'这种不带分区键的查询仍会扫描所有分区 - 示例(LIST 分区):
CREATE TABLE logs (id SERIAL, region TEXT, ts TIMESTAMP) PARTITION BY LIST (region); CREATE TABLE logs_us PARTITION OF logs FOR VALUES IN ('US'); CREATE TABLE logs_eu PARTITION OF logs FOR VALUES IN ('EU');
ClickHouse 的 ENGINE = ReplicatedReplacingMergeTree 分区键不是可选配置项
ClickHouse 中分区(PARTITION BY)是引擎定义的一部分,不能事后修改。你改了 PARTITION BY 就等于换了引擎逻辑,必须重建表、重导数据。
常见错误现象:用 INSERT 写入后查不到数据,或 SELECT 返回重复行 —— 很可能是分区键设计和 ORDER BY 不一致,导致 ReplacingMergeTree 无法正确去重。
- 使用场景:日志聚合、IoT 设备上报(按
toMonday(ts)或toYYYYMMDD(ts)分区) - 分区键必须是
ORDER BY的前缀,否则建表失败;同时要避免分区太细(如按秒)或太粗(如按年),前者产生海量小分区,后者丧失裁剪效果 - 兼容性注意:ZooKeeper 节点压力直接受分区数量影响;每多一个分区,元数据同步开销上升
- 示例(推荐写法):
CREATE TABLE events ( ts DateTime, device_id String, value Float64 ) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/{shard}/events', '{replica}') PARTITION BY toYYYYMM(ts) ORDER BY (ts, device_id);










