外观
OceanBase 分区表设计
分区类型
1. 范围分区
范围分区是最常用的分区类型,根据列值的范围将数据分配到不同的分区。适用于按时间、ID 等连续值进行分区的场景。
sql
-- 按时间范围分区示例
CREATE TABLE range_partition_table (
id INT PRIMARY KEY,
create_time DATETIME,
name VARCHAR(50)
) PARTITION BY RANGE (UNIX_TIMESTAMP(create_time)) (
PARTITION p202401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
PARTITION p202402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01 00:00:00')),
PARTITION p202403 VALUES LESS THAN (UNIX_TIMESTAMP('2024-04-01 00:00:00')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 按 ID 范围分区示例
CREATE TABLE id_range_partition (
id INT PRIMARY KEY,
name VARCHAR(50)
) PARTITION BY RANGE (id) (
PARTITION p1 VALUES LESS THAN (1000000),
PARTITION p2 VALUES LESS THAN (2000000),
PARTITION p3 VALUES LESS THAN (3000000),
PARTITION pmax VALUES LESS THAN MAXVALUE
);2. 哈希分区
哈希分区根据列值的哈希值将数据均匀分配到不同的分区,适用于数据分布比较均匀、查询模式比较随机的场景。
sql
-- 按 ID 哈希分区示例
CREATE TABLE hash_partition_table (
id INT PRIMARY KEY,
name VARCHAR(50)
) PARTITION BY HASH (id) PARTITIONS 8;
-- 按用户名哈希分区示例
CREATE TABLE user_hash_partition (
user_id INT PRIMARY KEY,
username VARCHAR(50),
email VARCHAR(100)
) PARTITION BY HASH (username) PARTITIONS 16;3. 列表分区
列表分区根据列值的预定义列表将数据分配到不同的分区,适用于列值是离散值的场景。
sql
-- 按地区列表分区示例
CREATE TABLE list_partition_table (
id INT PRIMARY KEY,
region VARCHAR(20),
name VARCHAR(50)
) PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('北京', '天津', '河北', '山西', '内蒙古'),
PARTITION p_east VALUES IN ('上海', '江苏', '浙江', '安徽', '福建', '江西', '山东'),
PARTITION p_south VALUES IN ('广东', '广西', '海南'),
PARTITION p_west VALUES IN ('重庆', '四川', '贵州', '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆'),
PARTITION p_other VALUES IN ('香港', '澳门', '台湾')
);
-- 按状态列表分区示例
CREATE TABLE status_list_partition (
order_id INT PRIMARY KEY,
order_status VARCHAR(20),
order_time DATETIME
) PARTITION BY LIST (order_status) (
PARTITION p_pending VALUES IN ('pending'),
PARTITION p_processing VALUES IN ('processing'),
PARTITION p_completed VALUES IN ('completed'),
PARTITION p_cancelled VALUES IN ('cancelled'),
PARTITION p_failed VALUES IN ('failed')
);4. 复合分区
复合分区是结合多种分区类型的分区方式,常见的复合分区包括:
- 范围-哈希复合分区:先按范围分区,再按哈希分区
- 范围-列表复合分区:先按范围分区,再按列表分区
- 哈希-范围复合分区:先按哈希分区,再按范围分区
sql
-- 范围-哈希复合分区示例
CREATE TABLE range_hash_partition (
id INT PRIMARY KEY,
create_time DATETIME,
region VARCHAR(20),
name VARCHAR(50)
) PARTITION BY RANGE (UNIX_TIMESTAMP(create_time)) SUBPARTITION BY HASH (region) SUBPARTITIONS 4 (
PARTITION p202401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
PARTITION p202402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01 00:00:00')),
PARTITION p202403 VALUES LESS THAN (UNIX_TIMESTAMP('2024-04-01 00:00:00')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 范围-列表复合分区示例
CREATE TABLE range_list_partition (
id INT PRIMARY KEY,
create_time DATETIME,
status VARCHAR(20),
name VARCHAR(50)
) PARTITION BY RANGE (UNIX_TIMESTAMP(create_time)) SUBPARTITION BY LIST (status) (
PARTITION p202401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')) (
SUBPARTITION p202401_active VALUES IN ('active'),
SUBPARTITION p202401_inactive VALUES IN ('inactive'),
SUBPARTITION p202401_deleted VALUES IN ('deleted')
),
PARTITION p202402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01 00:00:00')) (
SUBPARTITION p202402_active VALUES IN ('active'),
SUBPARTITION p202402_inactive VALUES IN ('inactive'),
SUBPARTITION p202402_deleted VALUES IN ('deleted')
)
);分区键选择
1. 分区键选择原则
- 查询相关性:分区键应与查询条件相关,以便查询时只访问相关分区
- 数据分布均匀性:分区键应使数据在各个分区中分布均匀,避免数据倾斜
- 更新频率:分区键应选择更新频率低的列,避免分区键更新导致的数据迁移
- 业务含义:分区键应具有明确的业务含义,便于数据管理和维护
- 基数:分区键的基数应适中,不宜过高或过低
2. 常见分区键选择
| 业务场景 | 推荐分区键 | 分区类型 | 优势 |
|---|---|---|---|
| 时序数据 | 时间列(如 create_time) | 范围分区 | 查询时可按时间范围过滤分区,提高查询性能 |
| 用户数据 | 用户 ID、用户名 | 哈希分区 | 数据分布均匀,支持水平扩展 |
| 地域数据 | 地区列 | 列表分区 | 便于按地区管理和查询数据 |
| 订单数据 | 订单 ID、订单时间 | 范围分区/复合分区 | 支持按订单 ID 或时间查询,提高查询性能 |
| 日志数据 | 日志时间、日志类型 | 范围-列表复合分区 | 便于按时间和类型管理日志数据 |
3. 分区键选择陷阱
- 避免使用低基数列:低基数列会导致数据分布不均,影响查询性能
- 避免使用频繁更新的列:频繁更新分区键会导致数据在分区间迁移,影响性能
- 避免使用 NULL 值较多的列:NULL 值会导致数据集中在一个分区,影响数据分布均匀性
- 避免使用过长的字符串列:过长的字符串列会增加哈希计算开销,影响性能
分区表设计最佳实践
1. 分区数量设计
- 合理控制分区数量:每个表的分区数量不宜过多,推荐控制在 1000 个以内
- 考虑节点数量:分区数量应与节点数量相匹配,便于数据均匀分布
- 考虑查询性能:过多的分区会增加查询计划生成时间,影响查询性能
- 考虑维护成本:过多的分区会增加维护成本,如备份恢复时间
2. 分区表命名规范
- 分区表命名:分区表名应体现分区类型和业务含义,如
order_range_partition - 分区命名:分区名应体现分区范围或值,如
p202401(范围分区)、p_north(列表分区) - 子分区命名:子分区名应体现父分区和子分区的特征,如
p202401_active(复合分区)
3. 分区表索引设计
- 全局索引:全局索引适用于跨分区查询,索引数据分布在所有分区
- 本地索引:本地索引适用于单分区查询,索引数据与分区数据存储在一起
- 索引分区键:本地索引的分区键应与表的分区键一致
- 唯一索引:唯一索引必须包含表的分区键
sql
-- 全局索引示例
CREATE GLOBAL INDEX idx_name ON range_partition_table (name);
-- 本地索引示例
CREATE LOCAL INDEX idx_create_time ON range_partition_table (create_time);
-- 唯一索引示例(必须包含分区键)
CREATE UNIQUE INDEX idx_id_create_time ON range_partition_table (id, UNIX_TIMESTAMP(create_time));4. 分区表查询优化
- 使用分区裁剪:在查询条件中包含分区键,以便只访问相关分区
- 避免全表扫描:通过合理的索引设计和查询条件,避免全表扫描
- 使用分区并行查询:对于大规模查询,启用并行查询提高性能
- 优化 join 操作:当 join 的表有相同的分区键时,使用分区 wise join 提高性能
sql
-- 分区裁剪示例
SELECT * FROM range_partition_table WHERE create_time BETWEEN '2024-01-01' AND '2024-01-31';
-- 分区并行查询示例
ALTER SESSION SET parallel_degree_policy = 'auto';
SELECT /*+ parallel(8) */ * FROM range_partition_table WHERE create_time < '2024-02-01';
-- 分区 wise join 示例
SELECT t1.*, t2.*
FROM range_partition_table t1
JOIN range_partition_table t2 ON t1.id = t2.id AND t1.create_time = t2.create_time
WHERE t1.create_time BETWEEN '2024-01-01' AND '2024-01-31';5. 分区表维护
- 定期清理历史数据:通过删除或合并旧分区,清理历史数据
- 定期合并分区:对于范围分区,定期合并小分区,提高查询性能
- 定期检查分区数据分布:检查数据分布是否均匀,避免数据倾斜
- 备份分区数据:支持按分区备份,提高备份恢复效率
sql
-- 删除旧分区示例
ALTER TABLE range_partition_table DROP PARTITION p202401;
-- 合并分区示例
ALTER TABLE range_partition_table COALESCE PARTITION 2;
-- 检查分区数据分布示例
SELECT table_name, partition_name, table_rows
FROM information_schema.partitions
WHERE table_name = 'range_partition_table'
ORDER BY partition_name;分区表管理操作
1. 创建分区表
sql
-- 创建范围分区表
CREATE TABLE partition_table (
id INT PRIMARY KEY,
create_time DATETIME,
name VARCHAR(50)
) PARTITION BY RANGE (UNIX_TIMESTAMP(create_time)) (
PARTITION p202401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
PARTITION p202402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01 00:00:00')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);2. 添加分区
sql
-- 为范围分区表添加分区
ALTER TABLE range_partition_table ADD PARTITION (
PARTITION p202404 VALUES LESS THAN (UNIX_TIMESTAMP('2024-05-01 00:00:00'))
);
-- 为列表分区表添加分区
ALTER TABLE list_partition_table ADD PARTITION (
PARTITION p_new VALUES IN ('new_region1', 'new_region2')
);
-- 为哈希分区表添加分区(增加分区数量)
ALTER TABLE hash_partition_table ADD PARTITION PARTITIONS 4;3. 删除分区
sql
-- 删除范围分区
ALTER TABLE range_partition_table DROP PARTITION p202401;
-- 删除列表分区
ALTER TABLE list_partition_table DROP PARTITION p_other;
-- 删除哈希分区(减少分区数量)
ALTER TABLE hash_partition_table COALESCE PARTITION 2;4. 拆分分区
sql
-- 拆分范围分区
ALTER TABLE range_partition_table SPLIT PARTITION pmax AT (UNIX_TIMESTAMP('2024-06-01 00:00:00')) INTO (
PARTITION p202405,
PARTITION pmax
);
-- 拆分列表分区
ALTER TABLE list_partition_table SPLIT PARTITION p_north INTO (
PARTITION p_north1 VALUES IN ('北京', '天津', '河北'),
PARTITION p_north2 VALUES IN ('山西', '内蒙古')
);5. 合并分区
sql
-- 合并范围分区
ALTER TABLE range_partition_table MERGE PARTITIONS p202401, p202402 INTO PARTITION p2024q1;
-- 合并列表分区
ALTER TABLE list_partition_table MERGE PARTITIONS p_north1, p_north2 INTO PARTITION p_north;6. 重命名分区
sql
-- 重命名范围分区
ALTER TABLE range_partition_table RENAME PARTITION p2024q1 TO p202401_02;
-- 重命名列表分区
ALTER TABLE list_partition_table RENAME PARTITION p_north TO p_northern;分区表性能优化
1. 分区裁剪优化
- 确保查询条件包含分区键:查询条件中包含分区键,以便查询时只访问相关分区
- 使用绑定变量:使用绑定变量可以提高查询计划缓存命中率,有利于分区裁剪
- 避免使用函数包装分区键:函数包装分区键会导致分区裁剪失效,如
DATE_FORMAT(create_time, '%Y-%m') = '2024-01' - 使用明确的分区键值:使用明确的分区键值,避免使用范围过大的查询条件
2. 数据分布优化
- 选择合适的分区键:选择使数据分布均匀的分区键
- 定期检查数据分布:定期检查各分区的数据分布情况,发现数据倾斜及时调整
- 使用分区键直方图:收集分区键的直方图统计信息,优化查询计划
- 调整分区数量:根据数据增长情况,调整分区数量,保持数据分布均匀
3. 索引优化
- 选择合适的索引类型:根据查询模式选择全局索引或本地索引
- 优化索引列顺序:将查询频率高的列放在索引前面
- 避免创建过多索引:过多的索引会影响写入性能
- 定期重建索引:定期重建索引,提高索引性能
4. 写入性能优化
- 批量写入:使用批量写入减少分区锁竞争,提高写入性能
- 避免热点分区:通过合理的分区键设计,避免写入时的热点分区
- 调整分区事务隔离级别:根据业务需求调整分区事务隔离级别,提高并发写入性能
- 使用并行写入:对于大规模数据导入,使用并行写入提高性能
常见问题(FAQ)
Q1: 如何选择合适的分区类型?
A1: 选择合适的分区类型需要考虑业务场景和查询模式:
- 对于时序数据,推荐使用范围分区
- 对于用户数据,推荐使用哈希分区
- 对于地域数据,推荐使用列表分区
- 对于复杂的业务场景,推荐使用复合分区
Q2: 分区表的分区数量越多越好吗?
A2: 不是。分区数量过多会导致以下问题:
- 查询计划生成时间增加,影响查询性能
- 元数据管理复杂度增加
- 备份恢复时间增加
- 内存占用增加
推荐每个表的分区数量控制在 1000 个以内。
Q3: 如何处理分区表的数据倾斜问题?
A3: 处理分区表的数据倾斜问题可以采取以下措施:
- 重新选择分区键,使数据分布均匀
- 调整分区数量,增加分区粒度
- 使用复合分区,结合多种分区类型
- 对热点数据进行特殊处理,如拆分热点分区
Q4: 分区表可以修改分区键吗?
A4: 不可以。分区键一旦确定,无法直接修改。如果需要修改分区键,需要重新创建表并迁移数据。
Q5: 如何查询分区表的分区信息?
A5: 可以通过以下方式查询分区表的分区信息:
sql
-- 查询分区表的分区信息
SELECT * FROM information_schema.partitions WHERE table_name = 'partition_table';
-- 查询分区表的分区键信息
SELECT * FROM information_schema.key_column_usage WHERE table_name = 'partition_table' AND constraint_name = 'PRIMARY';
-- 查询 OceanBase 特有的分区信息
SELECT * FROM oceanbase.__all_virtual_partition_info WHERE table_name = 'partition_table';Q6: 分区表的备份和恢复与普通表有什么区别?
A6: 分区表的备份和恢复与普通表的主要区别:
- 分区表支持按分区备份和恢复,可以只备份或恢复特定分区
- 分区表的备份和恢复速度更快,因为可以并行处理多个分区
- 分区表的备份和恢复灵活性更高,可以根据业务需求选择备份或恢复的分区
Q7: 如何将普通表转换为分区表?
A7: 将普通表转换为分区表可以采取以下方法:
- 创建一个新的分区表,结构与原表相同
- 将原表的数据导入到新的分区表
- 重命名原表和新表,完成转换
sql
-- 创建分区表
CREATE TABLE new_partition_table LIKE old_table;
ALTER TABLE new_partition_table PARTITION BY RANGE (id) (
PARTITION p1 VALUES LESS THAN (1000000),
PARTITION p2 VALUES LESS THAN (2000000),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 导入数据
INSERT INTO new_partition_table SELECT * FROM old_table;
-- 重命名表
RENAME TABLE old_table TO old_table_backup, new_partition_table TO old_table;Q8: 分区表的性能一定比普通表好吗?
A8: 不一定。分区表的性能取决于以下因素:
- 分区键设计是否合理
- 查询条件是否包含分区键
- 数据分布是否均匀
- 索引设计是否优化
如果分区表设计不合理,性能可能比普通表更差。因此,在设计分区表时,需要根据业务场景和查询模式进行合理设计。
