
核心问题 | 没有分区会怎么样 | 分区怎么解决问题 |
数据在哪里? | 所有数据堆积在一个节点,其他节点空闲 | 按照规则均匀分散到所有节点 |
查询去哪里找? | 每条 SQL 都要问所有节点 | 带分区键的查询直接定位到单个节点 |
怎么消除热点? | 新数据集中写入一个节点 | HASH 打散,写入均衡分布 |

-- ❌ 报错:A PRIMARY KEY must include all columns in the table's partitioning functionCREATE TABLE users (id BIGINT PRIMARY KEY,userid BIGINT) PARTITION BY HASH(userid) PARTITIONS 16;-- ✅ 正确:主键包含分区键CREATE TABLE users (id BIGINT AUTO_INCREMENT,userid BIGINT NOT NULL,PRIMARY KEY (userid)) PARTITION BY HASH(userid) PARTITIONS 16;

-- ❌ 写入热点:新数据永远写入最后一个分区CREATE TABLE t (id BIGINT AUTO_INCREMENT,...PRIMARY KEY (id)) PARTITION BY RANGE(id) (PARTITION p0 VALUES LESS THAN (100000),PARTITION p1 VALUES LESS THAN (200000),PARTITION p_maxvalue VALUES LESS THAN MAXVALUE -- 所有新数据都在这里);-- ✅ RANGE 按时间管理生命周期 + HASH 打散写入) PARTITION BY RANGE COLUMNS(created_at)SUBPARTITION BY HASH(userid) SUBPARTITIONS 6 (...);
-- ✅ 只访问1个节点,毫秒级返回SELECT * FROM orders WHERE userid = 12345;-- ❌ 访问所有节点,每个节点都要查一遍SELECT * FROM orders WHERE orderno = 'ORD-001';
类型 | 原理 | 适用场景 | 示例 |
HASH | 对整数取模均匀打散 | 用户表、订单表、通用业务表 | PARTITION BY HASH(userid) |
KEY | 对任意类型内部 hash 打散 | 分区键是 VARCHAR 的场景 | PARTITION BY KEY(orderno) |
RANGE | 按值的范围划分 | 日志表、流水表、需要按时间清理的表 | PARTITION BY RANGE COLUMNS(created_at) |
RANGE + HASH | 一级按范围,二级再打散 | 大流水表:既要按时间清理,又要打散写入 | |
当前节点数 | 预期扩容后的节点数 | 推荐分区数 |
3节点 | 6~8 | 16 |
6节点 | 12 | 24 |
CREATE TABLE orders (...KEY idx_order_no (orderno) -- 不含分区键 userid 的 LOCAL 索引) PARTITION BY HASH(userid) PARTITIONS 16;
SELECT * FROM orders WHERE orderno = 'ORD-2026-001';
查询方式 | 访问分区数 | 网络 RPC | 适合场景 |
WHERE userid = X | 1 | 1次 | 高频查询 |
WHERE orderno = X(LOCAL 索引) | 全部 | N 次 | 低频查询、结果集小 |
无索引全表扫描 | 全部 | N 次 | ❌ 永远不要 |
-- 从这样SELECT * FROM orders WHERE orderno = 'ORD-001';-- 改成这样(如果业务能拿到 userid)SELECT * FROM orders WHERE userid = 123 AND orderno = 'ORD-001';
| 按 user_id 分区 | 按 order_no 分区 |
查用户订单列表 | ✅ 单分区,天然聚合 | ❌ 散落在各分区 |
按单号查一笔 | LOCAL 索引,扇出所有分区(但每个分区最多1行,可接受) | ✅ 单分区 |
写入均匀性 | ✅ userid 整数,HASH 散列效果好 | ⚠️ orderno 常含日期前缀,散列可能不均 |
分区类型 | HASH(整数) | KEY(字符串,HASH 不支持 VARCHAR) |
单机 MySQL | TDSQL Boundless | 为什么改 |
id BIGINT AUTO_INCREMENT PRIMARY KEY | 不再依赖自增 ID,业务键做主键 | 避免写入热点 |
没有分区 | 加 PARTITION BY HASH(业务键1, 业务键2) | 数据打散到多节点 |
DELETE WHERE date < '...' 清理历史 | 改用 DROP PARTITION | 毫秒完成 vs 锁表几小时 |
随便建索引 | 核心查询的索引要包含分区键 | 避免全分区扫描 |
-- 订单表, 改造前( MySQL)CREATE TABLE orders (id BIGINT AUTO_INCREMENT PRIMARY KEY,userid BIGINT,orderno VARCHAR(64),amount DECIMAL(10,2),status TINYINT,created_at DATETIME,KEY idx_order_no (orderno),KEY idx_created (created_at));-- 订单表, 改造后(TDSQL Boundless)CREATE TABLE orders (id BIGINT AUTO_INCREMENT,userid BIGINT NOT NULL,orderno VARCHAR(64) NOT NULL,amount DECIMAL(10,2),status TINYINT,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (userid, orderno), -- 分区键在主键第一位KEY idx_order_no (orderno), -- 单号索引解决根据订单号的检索操作KEY idx_user_created (userid, created_at) -- 用户订单列表查询) PARTITION BY HASH(userid) PARTITIONS 16;
-- ✅ 不用改,天然命中分区SELECT * FROM orders WHERE userid = 123 ORDER BY created_at DESC LIMIT 20;SELECT * FROM orders WHERE userid = 123 ORDER BY orderno DESC LIMIT 20;
-- 最优场景,带上用户 userid 查询订单号SELECT * FROM orders WHERE userid = 123 AND orderno = 'ORD-001';-- 可接受(LOCAL 索引,16次索引查找)SELECT * FROM orders WHERE orderno = 'ORD-001';
CREATE TABLE dim_city (city_id INT PRIMARY KEY,city_name VARCHAR(64)) sync_level = node(all) distribution = node(all);
表类型 | 适用场景 | 特点 |
普通表(单表) | 数据量小、无分布需求 | 所有数据在一个复制组内 |
分区表 | 大数据量、需水平扩展 | 数据按规则分布到多个服务组内 |
同步表 | 系统配置、维度表、参数表、读多写少小表 | 全节点强同步副本,若频繁写入可能出现写入卡顿,如 Follower 故障时卡一个 Lease |
-- 带分区键EXPLAIN SELECT * FROM orders WHERE userid = 12345;*************************** 1. row ***************************id: 1select_type: SIMPLEtable: orderspartitions: p1type: refpossible_keys: PRIMARYkey: PRIMARYkey_len: 8ref: constrows: 2filtered: 100.00Extra: NULL-- 不带分区键orders*************************** 1. row ***************************id: 1select_type: SIMPLEtable: orderspartitions: p0,p1,p2,p3,p4,p5,p6,p7type: refpossible_keys: idx_order_nokey: idx_order_nokey_len: 258ref: constrows: 9filtered: 100.00Extra: NULL
错误示例 | 错误影响 | 正确做法 |
自增 ID 做主键与分区键 | 写入热点 | 改用业务字段做分区键与主键;order by 排序时,改用业务时间或者其他天然有序的字段排序 |
查询不带分区键 | 全分区扫描 | WHERE 条件补上分区键 |
UPDATE 分区键的值 | 跨分区迁移,可能失败 | 改用 DELETE + INSERT 代替直接 UPDATE |
VARCHAR 用 HASH 分区 | 建表报错 | 改用 KEY 分区 |
RANGE 按天分区保留多年 | 1000+分区,元数据膨胀 | 改为按月(36个分区/3年) |
文档反馈