一、分区表的前世今生

数据库分区表就像把一个大衣柜分成不同隔间,每个隔间存放特定季节的衣服。这样做不仅找衣服更方便,还能避免所有衣服堆在一起造成的杂乱无章。PolarDB作为阿里云自研的云原生数据库,在分区表功能上做了大量优化,让这个"智能衣柜"变得更好用。

传统单表就像把所有数据堆在一个大仓库里,当数据量超过千万级时,查询性能会明显下降。而分区表通过将数据分散到多个物理存储单元,实现了"分而治之"的效果。比如我们有个电商订单表,可以按订单日期分成12个月的分区,查询某个月的数据时只需要扫描对应分区,效率自然就上去了。

二、三大分区策略详解

1. 范围分区(Range Partitioning)

范围分区就像按时间轴整理档案,特别适合有明显时间维度的数据。以下是创建范围分区表的示例:

-- PolarDB MySQL版范围分区表示例
CREATE TABLE sales (
    id INT AUTO_INCREMENT,
    sale_date DATE NOT NULL,
    product_id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY (id, sale_date)
) PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p2020 VALUES LESS THAN (2021),
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

-- 插入数据时会自动路由到对应分区
INSERT INTO sales (sale_date, product_id, amount) 
VALUES ('2021-06-15', 1001, 2999.00);

这个例子中,我们按年份划分销售数据,2020年的数据会进入p2020分区,2021年的进入p2021分区,以此类推。MAXVALUE是个特殊值,用于捕获所有超出定义范围的数据。

适用场景

  • 时间序列数据(日志、交易记录)
  • 需要定期归档历史数据的场景
  • 有明显范围特征的业务数据

注意事项

  • 要合理设置分区边界,避免出现"热点分区"
  • 考虑未来数据增长,预留足够分区空间
  • 定期维护过期分区(如删除或归档)

2. 哈希分区(Hash Partitioning)

哈希分区就像抽签分班,通过哈希算法把数据均匀分散到各个分区。下面是个用户表哈希分区的例子:

-- PolarDB PostgreSQL版哈希分区表示例
CREATE TABLE users (
    user_id BIGSERIAL,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id)
) PARTITION BY HASH (user_id);

-- 创建4个哈希分区
CREATE TABLE users_p0 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE users_p1 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE users_p2 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE users_p3 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 3);

技术特点

  • 数据分布均匀,避免倾斜
  • 适合等值查询,特别是点查场景
  • 不支持范围查询裁剪

优化技巧

  • 选择离散性好的列作为分区键
  • 分区数量建议为2的N次方
  • 避免频繁更新的列作为分区键

3. 列表分区(List Partitioning)

列表分区就像按省份分类客户资料,每个分区包含明确的离散值集合。看看这个地区销售数据的例子:

-- PolarDB MySQL版列表分区表示例
CREATE TABLE regional_sales (
    id INT AUTO_INCREMENT,
    region VARCHAR(20) NOT NULL,
    sale_date DATE,
    amount DECIMAL(10,2),
    PRIMARY KEY (id, region)
) PARTITION BY LIST COLUMNS(region) (
    PARTITION p_east VALUES IN ('Shanghai', 'Jiangsu', 'Zhejiang'),
    PARTITION p_north VALUES IN ('Beijing', 'Tianjin', 'Hebei'),
    PARTITION p_south VALUES IN ('Guangdong', 'Fujian', 'Hainan'),
    PARTITION p_other VALUES IN (DEFAULT)
);

-- 插入数据时会根据region值进入对应分区
INSERT INTO regional_sales (region, sale_date, amount)
VALUES ('Guangdong', '2023-01-10', 5000.00);

业务场景

  • 有明显地域特征的数据
  • 按固定类别划分的数据(如产品类型、客户等级)
  • 枚举值相对固定的场景

特殊优势

  • 支持多列组合分区(LIST COLUMNS)
  • 可以明确控制每条数据的归属分区
  • 默认分区(DEFAULT)可以捕获未定义值

三、性能优化实战技巧

1. 分区裁剪(Partition Pruning)

分区裁剪是分区表最重要的性能优化手段,就像查字典时直接翻到正确的部首,而不是整本字典从头翻到尾。看这个例子:

-- 这个查询只会扫描p2021分区
EXPLAIN SELECT * FROM sales 
WHERE sale_date BETWEEN '2021-01-01' AND '2021-12-31';

-- 而全表扫描会访问所有分区
EXPLAIN SELECT * FROM sales WHERE amount > 1000;

优化要点

  • WHERE条件要包含分区键
  • 避免对分区键使用函数转换(会阻止裁剪)
  • 使用EXPLAIN验证是否发生裁剪

2. 并行查询加速

PolarDB支持跨分区并行查询,就像多个工人同时整理不同柜子:

-- PolarDB PostgreSQL并行查询示例
SET max_parallel_workers_per_gather = 8;
SELECT product_id, SUM(amount) FROM sales
WHERE sale_date BETWEEN '2021-01-01' AND '2021-12-31'
GROUP BY product_id;

配置建议

  • 合理设置并行度(通常为CPU核数的50-70%)
  • 大查询才适合并行,简单查询可能适得其反
  • 监控系统资源使用情况

3. 分区维护自动化

定期维护分区就像整理衣柜换季,可以通过存储过程实现自动化:

-- PolarDB MySQL版自动添加分区示例
DELIMITER //
CREATE PROCEDURE auto_add_partition(IN schema_name VARCHAR(64), IN table_name VARCHAR(64))
BEGIN
    DECLARE next_year INT;
    DECLARE add_stmt TEXT;
    
    SET next_year = YEAR(CURDATE()) + 1;
    SET add_stmt = CONCAT('ALTER TABLE ', schema_name, '.', table_name, 
                         ' ADD PARTITION (PARTITION p', next_year, 
                         ' VALUES LESS THAN (', next_year + 1, '))');
    
    PREPARE stmt FROM add_stmt;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

-- 每月执行一次检查
CALL auto_add_partition('mydb', 'sales');

四、选型指南与避坑建议

1. 分区策略选型矩阵

策略类型 适合场景 不适合场景 典型TPS
范围分区 时间序列、连续数值 离散值、频繁更新的列 5000-20000
哈希分区 均匀分布、点查为主 范围查询、需要排序 10000-30000
列表分区 离散值、固定分类 值不固定、高基数 3000-15000

2. 常见问题解决方案

热点分区问题

  • 现象:某个分区访问特别频繁
  • 解决方案:对于哈希分区可以增加分区数;对于列表分区可以考虑进一步细分

跨分区查询变慢

  • 现象:不指定分区键的查询比单表还慢
  • 解决方案:建立全局索引或使用分区聚合表

分区键选择困难

  • 评估维度:
    1. 数据分布均匀性
    2. 查询模式匹配度
    3. 未来可扩展性

3. 进阶技巧

子分区(复合分区)

-- PolarDB MySQL版范围+哈希子分区示例
CREATE TABLE sensor_data (
    id BIGINT,
    device_id INT,
    collect_time DATETIME,
    value DOUBLE,
    PRIMARY KEY (id, collect_time, device_id)
) PARTITION BY RANGE (YEAR(collect_time))
  SUBPARTITION BY HASH(device_id)
  SUBPARTITIONS 4 (
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024)
);

本地索引与全局索引

  • 本地索引:每个分区单独维护,适合分区内查询
  • 全局索引:跨分区统一维护,适合不指定分区键的查询

五、总结与展望

分区表是应对海量数据的神兵利器,但要用好它需要理解不同策略的特点。范围分区适合时间序列,哈希分区擅长均匀分布,列表分区则应对离散分类。PolarDB在原生分区表基础上,通过智能裁剪、并行查询等优化,让分区表性能更上一层楼。

未来随着硬件发展,分区表可能会与列存、内存计算等技术深度融合。但核心原则不变:正确的分区策略+合理的查询方式=极致的性能表现。建议在实际应用中从小规模开始验证,逐步找到最适合业务的分区方案。