二、什么时候应该使用分区表?
如果你的业务符合以下任一特征,建议选择分区表:
1. 数据量大或高并发写入:需要支持极高的写入吞吐(TPS),单机无法承载。
2. 数据无限增长:业务处于快速发展期,无法预估未来的数据上限。
三、选择分区策略
TDSQL Boundless 支持如下几种分区策略,选择正确的策略是建表最关键的一步。
1. HASH 分区
·简介:对分区键值进行取模运算,将数据随机且均匀地打散。
·适用场景:最推荐的默认选择。适用于分区键为整数 (INT / BIGINT) 的场景,如用户 ID、纯数字订单号。
·优势:负载均衡效果好,写入性能高。
2. KEY 分区
·简介:类似 HASH,但支持除 BLOB / TEXT 外的多种数据类型,系统使用内部哈希函数处理数据。
·适用场景:分区键为字符串 (VARCHAR / CHAR) 的场景,如 UUID、身份证号、业务流水号。
·优势:解决了字符串无法直接进行 HASH 取模的问题,无需业务层转换。
3. RANGE 分区
·简介:根据键值的连续区间(如时间段、ID 段)划分数据。
·适用场景:时间序列数据,如日志、流水、监控等。
·优势:范围查询快,可以通过 DROP PARTITION 秒级删除过期的历史数据。
4. LIST 分区
·简介:根据枚举值(离散列表)划分数据。
·适用场景:具有明确类别属性的数据,如地域(省份、城市)、租户 ID。
·优势:数据归类清晰,便于按类别隔离管理。
·风险:数据倾斜。如果某个类别(如 “广东省”)的数据量远超其他类别,该节点会成为瓶颈。
5. 二级分区
·简介:在已有的一级分区基础上,对每个分区再次进行细分。与 MySQL 8.0 规范保持一致,一级分区仅支持 RANGE 或 LIST,二级分区仅支持 HASH 或 KEY。
·适用场景:数据量极大,且具有多维度的查询和管理需求。例如,先按时间(RANGE)进行一级分区以便于历史数据清理,再按用户 ID(HASH/KEY)进行二级分区以打散热点数据,提升并发写入性能。
·优势:结合了范围/列表分区的管理便利性和哈希/键分区的负载均衡能力。
·风险:总分区数为“一级 × 二级”,过多分区会急剧增加系统的元数据管理开销,消耗大量内存和文件句柄,并导致 DDL 操作变慢。建议单表总分区数控制在8192以内。
五、场景 A:标准电商订单(使用 HASH)
分区键为纯数字 ID。
CREATE TABLE `mall`.`orders` ( `order_id` bigint NOT NULL, `user_id` bigint NOT NULL, `amount` decimal(10,2), `create_time` datetime DEFAULT CURRENT_TIMESTAMP, — 主键必须包含分区键 PRIMARY KEY (`order_id`))PARTITION BY HASH(`order_id`)PARTITIONS 64;
六、场景 B:用户中心(使用 KEY)
分区键为 UUID 字符串,使用 KEY 分区自动处理哈希。
CREATE TABLE `user_center`.`user_profiles` ( `user_uuid` varchar(36) NOT NULL, `nick_name` varchar(50), `register_time` datetime, PRIMARY KEY (`user_uuid`))PARTITION BY KEY(`user_uuid`)PARTITIONS 64;
七、场景 C:操作日志(按月 RANGE 分区)
适用于操作日志、流水、监控等时间序列数据,且需要定期按时间清理的场景。
TDSQL Boundless 当前支持的分区类型为 RANGE、RANGE COLUMNS、LIST、LIST COLUMNS、HASH、KEY。
注意:针对时间序列场景,普通 RANGE 分区不支持自动预创建未来分区。建表时需要预定义未来一段时间的分区,并通过运维手段(外部调度任务)周期性预创建新分区。若不希望人工维护分区创建节奏,可改用 创建 INTERVAL RANGE 分区,声明固定步长后系统会在写入越界数据时自动创建所需分区。
八、建表示例:预定义 RANGE 分区
CREATE TABLE `sys`.`audit_logs` ( `log_id` bigint NOT NULL, `content` text, `log_time` datetime NOT NULL, PRIMARY KEY (`log_id`, `log_time`))PARTITION BY RANGE COLUMNS(`log_time`) ( PARTITION p202601 VALUES LESS THAN ('2026-02-01'), PARTITION p202602 VALUES LESS THAN ('2026-03-01'), PARTITION p202603 VALUES LESS THAN ('2026-04-01'));
九、RANGE 分区常用维护操作
| 操作场景 | SQL 示例 | 说明 |
| 删除过期分区 | ALTER TABLE sys.audit_logs DROP PARTITION p202601; | 秒级清理过期数据,性能远优于 DELETE |
| 清空分区数据 | ALTER TABLE sys.audit_logs TRUNCATE PARTITION p202601; | 保留分区结构,仅清空数据 |
| 预创建下月分区 | ALTER TABLE sys.audit_logs ADD PARTITION (PARTITION p202604 VALUES LESS THAN ('2026-05-01')); | 仅可在末尾新增,值必须严格递增 |
| 拆分 MAXVALUE 分区 | ALTER TABLE sys.audit_logs REORGANIZE PARTITION p_future INTO (PARTITION p202603 VALUES LESS THAN ('2026-04-01'), PARTITION p_future VALUES LESS THAN (MAXVALUE)); | 误用 MAXVALUE 后的补救手段 |
| 查看分区列表 | SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA='sys' AND TABLE_NAME='audit_logs'; | 查询当前分区状态与数据量 |
十、推荐方案:外部周期性预创建分区或使用 INTERVAL RANGE 分区
| 方案 | 适用场景 | 说明 |
| 外部调度任务执行 ALTER TABLE … ADD PARTITION | 已有 RANGE 表、不希望变更分区语法 | 需自行维护调度任务,遗漏会导致写入报错 |
| 声明 INTERVAL(…) 自动建分区 | 新建表,或可接受对已有表追加 INTERVAL 属性 | 由系统在 INSERT 越界时自动创建分区,无需外部调度,详见 创建 INTERVAL RANGE 分区 |
十一、场景 D:按地域分布(使用 LIST)
业务明确按城市划分,且不同城市数据隔离。
CREATE TABLE `crm`.`customers` ( `cust_id` bigint NOT NULL, `city_id` int NOT NULL, `name` varchar(50), PRIMARY KEY (`cust_id`, `city_id`))PARTITION BY LIST(`city_id`) ( PARTITION p_bj VALUES IN (10), PARTITION p_sh VALUES IN (20), PARTITION p_others VALUES IN (30));
十二、场景 E:海量历史流水表(使用 RANGE + HASH 二级分区)
适用于既需要按时间周期清理历史数据,又面临极高并发写入、需要将单月数据进一步打散的场景。
一级分区按月 RANGE 分区,二级分区按用户 ID HASH 分区。
CREATE TABLE `sys`.`massive_logs` ( log_id bigint NOT NULL, user_id bigint NOT NULL, content text, log_time datetime NOT NULL, PRIMARY KEY (log_id, log_time, user_id)) PARTITION BY RANGE COLUMNS(log_time) SUBPARTITION BY HASH(user_id) SUBPARTITIONS 4 (PARTITION p202601 VALUES LESS THAN ('2026-02-01'), PARTITION p202602 VALUES LESS THAN ('2026-03-01'), PARTITION p202603 VALUES LESS THAN ('2026-04-01') );
十三、关键约束
分区表有一个必须遵守的硬性约束,违反该约束将导致建表失败:
主键和 LOCAL 唯一索引必须包含分区键,GLOBAL 唯一索引无此约束。
·错误示例:
— 错误:分区键 user_id 没有包含在主键中 CREATE TABLE `error_table` ( `id` bigint NOT NULL PRIMARY KEY, `user_id` bigint NOT NULL ) PARTITION BY HASH(`user_id`); — 错误:分区键 user_id 没有包含在 LOCAL 唯一索引中 CREATE TABLE `error_table` ( `id` bigint NOT NULL, `user_id` bigint NOT NULL, `email` varchar(64) NOT NULL, PRIMARY KEY (`id`, `user_id`), UNIQUE KEY `uk_email` (`email`) ) PARTITION BY HASH(`user_id`);
·正确示例:
— 正确:将 user_id 加入联合主键 CREATE TABLE `correct_table` ( `id` bigint NOT NULL, `user_id` bigint NOT NULL, PRIMARY KEY (`id`, `user_id`) ) PARTITION BY HASH(`user_id`); — 正确:将 user_id 加入联合 Local 唯一索引 CREATE TABLE `correct_table` ( `id` bigint NOT NULL, `user_id` bigint NOT NULL, `email` varchar(64) NOT NULL, PRIMARY KEY (`id`, `user_id`), UNIQUE KEY `uk_email` (`email`, `user_id`) ) PARTITION BY HASH(`user_id`); — 正确:Global 唯一索引无需包含分区键 CREATE TABLE `correct_table` ( `id` bigint NOT NULL, `user_id` bigint NOT NULL, `email` varchar(64) NOT NULL, PRIMARY KEY (`id`, `user_id`), UNIQUE KEY `uk_email` (`email`) GLOBAL ) PARTITION BY HASH(`user_id`);
十四、实践建议
1. 分区数量
·策略:预分片不要仅考虑当前的物理节点数,需同时考虑未来集群规模和预期数据总量。
·推荐值:建议设置为 “预期未来最大节点数 × 2”,以确保数据充分分散。
2. 避免修改分区键
·尽量避免 UPDATE 语句修改分区键的值,这可能会导致数据行在物理节点间迁移(DELETE + INSERT),代价较高。
3. 查询优化
·带上分区键:查询时尽量在 WHERE 子句中带上分区键(如 WHERE user_id = ?),TDSQL Boundless 可以直接路由到特定节点,性能极高。
·避免全表扫描:不带分区键且没有命中 GLOBAL 索引的查询会触发广播扫描 (Scatter-Gather),查询所有节点并汇总,消耗大量资源。GLOBAL 索引使用建议见 全局二级索引。
十六、HASH 和 KEY 分区有什么区别?
HASH 主要处理整数,算法简单直接;KEY 主要处理字符串(也支持整数),内部使用复杂的哈希函数。如果是字符串 ID,请直接使用 KEY 分区。
十七、可以使用多个列做分区键吗?
可以,例如 PARTITION BY HASH(col1 + col2) 或 KEY(col1, col2)。但这会增加复杂性,通常建议使用单一的高基数列作为分区键。
十八、建表后可以修改分区键或分区类型吗?
极不推荐。虽然语法上支持,但这本质上是一次全量数据的 “重新洗牌”,会消耗巨大的系统资源(IO 与 CPU),并可能导致长时间的业务阻塞。
十九、RANGE 分区设了 MAXVALUE 兜底,下个月会自动建分区吗?
不会。MAXVALUE 是静态兜底分区,新数据只会落入该分区,不会自动按月分裂。若使用的是普通 RANGE 分区,需要通过外部调度任务定期执行 ALTER TABLE … ADD PARTITION 来预创建下一个时间段的分区;若希望免去人工维护,可改用 INTERVAL RANGE 分区,由系统在写入越界数据时自动创建分区。
20、分区表能修改分区策略吗?
可以,通过 ALTER TABLE … REMOVE PARTITIONING 移除分区后重新定义,或者直接 ALTER TABLE … PARTITION BY RANGE COLUMNS(…) (…) 重建分区策略。注意:重建分区会涉及数据重组,大表请评估代价并选择业务低峰期执行。
21、写入了超过最后一个分区上界的数据会发生什么?
如果建表时未定义 MAXVALUE 兜底分区,写入会报错 ER_NO_PARTITION_FOR_GIVEN_VALUE,提示 “Table has no partition for value xxx”。这是预期行为,可以作为 “忘记预创建分区”的兜底告警信号,避免数据静默落入 MAXVALUE 分区造成后续清理困难。
22、相关文档
·分区表实践教程
·创建 INTERVAL RANGE 分区