概述
分区表(Partition Table)是 TDSQL Boundless 处理海量数据和实现水平扩展的核心手段。与单表不同,分区表通过分区键(Partition Key)将数据打散分布到集群中的多个存储节点上,使数据库能够利用集群整体的计算能力和存储空间,突破单机的性能瓶颈。
什么时候应该使用分区表?
如果您的业务符合以下任一特征,建议选择分区表:
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'));
注意:
不建议使用
PARTITION p_future VALUES LESS THAN (MAXVALUE) 作为兜底分区。MAXVALUE 是静态兜底分区,不会随时间自动按月分裂。一旦写入超出最后一个分区上界的数据,所有新数据会持续落入 p_future,导致:p_future 无限膨胀,失去按月清理的能力;后续若想
ADD PARTITION p202304,会因 MAXVALUE 已占据末尾而直接报错,必须先 REORGANIZE PARTITION p_future,操作复杂且代价高。推荐做法:建表时预定义未来3 - 6个月的分区,并配合定时任务每月底提前
ADD PARTITION 预创建下一个月分区。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 分区,由系统在写入越界数据时自动创建分区。分区表能修改分区策略吗?
可以,通过
ALTER TABLE ... REMOVE PARTITIONING 移除分区后重新定义,或者直接 ALTER TABLE ... PARTITION BY RANGE COLUMNS(...) (...) 重建分区策略。注意:重建分区会涉及数据重组,大表请评估代价并选择业务低峰期执行。写入了超过最后一个分区上界的数据会发生什么?
如果建表时未定义
MAXVALUE 兜底分区,写入会报错 ER_NO_PARTITION_FOR_GIVEN_VALUE,提示 “Table has no partition for value xxx”。这是预期行为,可以作为 “忘记预创建分区”的兜底告警信号,避免数据静默落入 MAXVALUE 分区造成后续清理困难。相关文档
分区表实践教程