分区优势
-
管理大表:当单表数据达到数亿行、几十 GB 以上时,用分区能把数据分散到多个逻辑/物理单元,易维护。
-
提高查询性能(在能走分区剪枝时):针对分区键的查询可只扫描少数分区(Partition Pruning),显著减少 IO。
-
快速归档/删除 :删除旧数据可以通过
DROP PARTITION或EXCHANGE PARTITION实现"常数时间"删除,而不用DELETE全表扫描。 -
并行化/并发:某些存储引擎/环境下分区可促进并行 I/O(不是万能的)。
-
局部重建/维护 :可以对单个分区做
OPTIMIZE、备份、恢复,降低维护成本。
分区示例
sql
-- 创建订单表,并按 created_at 字段按年份分区
CREATE TABLE orders (
id BIGINT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
created_at DATE NOT NULL,
amount DECIMAL(10,2),
-- 必须包含分区列在主键中
PRIMARY KEY (id, created_at)
)
PARTITION BY RANGE COLUMNS(created_at) (
PARTITION p2022 VALUES LESS THAN ('2023-01-01'),
PARTITION p2023 VALUES LESS THAN ('2024-01-01')
);
-- 插入数据
INSERT INTO orders (user_id, created_at, amount)
VALUES
(1, '2022-03-10', 100.00),
(2, '2022-06-15', 250.50),
(3, '2023-01-22', 120.75),
(4, '2023-08-09', 350.25);
-- 查看表信息
SHOW CREATE TABLE orders
-- 为 2025 年的数据添加分区
-- LESS THAN = 分区接收"所有小于某个值"的数据
ALTER TABLE orders
ADD PARTITION (PARTITION p2025 VALUES LESS THAN ('2026-01-01'));
-- 插入 2025 年的数据(如果2025年的分区还没有创建好,数据不能进入特定的分区)
-- 添加会报错 ERROR 1526 (HY000): Table has no partition for value ...
INSERT INTO orders (user_id, created_at, amount)
VALUES
(6, '2025-02-10', 450.00),
(7, '2025-03-20', 300.00);
-- 删除 2022 年的分区(删除2022年分区的时候,2022年的数据也会被删除)
ALTER TABLE orders
DROP PARTITION p2022;
-- 查询 2023 年的数据
SELECT * FROM orders
WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';
Java实现分区
1.创建一个定时器(每天定时查询分区是否存在,不存在则创建分区)
java
@XxlJob("AUTO_CREATE_PARTITION")
public void autoCreatePartition() {
businessUserRecallReachService.autoCreatePartition();
}
2.查询分区是否存在
java
select partition_name from information_schema.partitions
where table_name='business_user_recall_reach_record' and partition_name=#{partitionName} limit 1
3.不存在,创建分区
java
ALTER TABLE business_user_recall_reach_record add PARTITION (PARTITION ${partitionName} VALUES LESS THAN (${partitionDate}))
代码:
java
/**
* 初始化按月分区
*
* @param model 数据模型
*/
private void initializeMonthPartition(Class<?> model) {
LocalDate now = LocalDate.now();
String name = "p" + DateUtils.month2number(now.plusMonths(1));
String value = now.plusMonths(2).format(DateTimeFormatter.ofPattern("yyyy-MM-01"));
String table = SQLUtils.unquote(TableInfoHelper.getTableInfo(model).getTableName());
if (!this.baseMapper.hasPartition(table, name)) {
this.baseMapper.addPartition(table, name, value);
}
}