CREATE TABLE logs ( id BIGINT AUTO_INCREMENT, log_time DATETIME NOT NULL, message VARCHAR(500), PRIMARY KEY (id, log_time) ) PARTITIONBYRANGE (YEAR(log_time)) ( PARTITION p2022 VALUES LESS THAN (2023), PARTITION p2023 VALUES LESS THAN (2024), PARTITION p2024 VALUES LESS THAN (2025), PARTITION p_future VALUES LESS THAN MAXVALUE );
适用场景:
时间序列数据(日志、订单、监控数据)
按日期归档和清理
#
2.2 LIST分区
按离散值分区:
CREATE TABLEuser ( id INT, region VARCHAR(20), name VARCHAR(50) ) PARTITIONBY LIST COLUMNS(region) ( PARTITION p_north VALUESIN ('北京', '天津', '河北'), PARTITION p_east VALUESIN ('上海', '江苏', '浙江'), PARTITION p_south VALUESIN ('广东', '福建', '海南'), PARTITION p_other VALUESIN (DEFAULT) );
CREATE TABLEuser ( id INT, name VARCHAR(50) ) PARTITIONBY KEY() PARTITIONS 4;
#
2.5 子分区(复合分区)
先按RANGE分区,再按HASH子分区:
CREATE TABLE logs ( id BIGINT, log_time DATETIME, message TEXT ) PARTITIONBYRANGE (YEAR(log_time)) SUBPARTITION BY HASH(MONTH(log_time)) SUBPARTITIONS 12 ( PARTITION p2023 VALUES LESS THAN (2024), PARTITION p2024 VALUES LESS THAN (2025) );
三、分区表的优势
#
3.1 查询优化
分区裁剪(Partition Pruning):
SELECT*FROM logs WHERE log_time >='2024-01-01'AND log_time <'2024-02-01';
-- 错误:分区键不是主键的一部分 CREATE TABLE logs ( id BIGINTPRIMARY KEY, log_time DATETIME ) PARTITIONBYRANGE (YEAR(log_time)) ... -- ERROR 1503: A PRIMARY KEY must include all columns in the table's partitioning function
CREATE TABLE system_logs ( id BIGINT UNSIGNED AUTO_INCREMENT, create_time DATETIME NOT NULL, level VARCHAR(10) NOT NULL, moduleVARCHAR(50), message TEXT, PRIMARY KEY (id, create_time), KEY idx_level_time (level, create_time) ) ENGINE=InnoDB PARTITIONBYRANGE (TO_DAYS(create_time)) ( PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')), PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')), PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')), PARTITION p_future VALUES LESS THAN MAXVALUE );
#
5.2 自动维护脚本
-- 每月初添加新分区 DELIMITER // CREATEPROCEDURE add_month_partition() BEGIN DECLARE next_month DATE; DECLARE partition_name VARCHAR(20); DECLARE less_than_value INT; SET next_month = DATE_ADD(DATE_FORMAT(CURDATE() +INTERVAL1MONTH, '%Y-%m-01'), INTERVAL1MONTH); SET partition_name = CONCAT('p', DATE_FORMAT(CURDATE() +INTERVAL1MONTH, '%Y%m')); SET less_than_value = TO_DAYS(next_month); SET@sql= CONCAT( 'ALTER TABLE system_logs REORGANIZE PARTITION p_future INTO ( PARTITION ', partition_name, ' VALUES LESS THAN (', less_than_value, '), PARTITION p_future VALUES LESS THAN MAXVALUE )' ); PREPARE stmt FROM@sql; EXECUTE stmt; DEALLOCATEPREPARE stmt; END// DELIMITER ;
-- 创建定时事件 CREATE EVENT add_partition_event ON SCHEDULE EVERY1MONTH STARTS '2024-02-01 00:00:00' DO CALL add_month_partition();
#
5.3 定期清理旧分区
-- 删除3个月前的分区 DELIMITER // CREATEPROCEDURE drop_old_partitions() BEGIN DECLARE done INTDEFAULTFALSE; DECLARE partition_name VARCHAR(20); DECLARE cur CURSORFOR SELECT partition_name FROM information_schema.partitions WHERE table_name ='system_logs' AND partition_name !='p_future' AND partition_name < CONCAT('p', DATE_FORMAT(CURDATE() -INTERVAL3MONTH, '%Y%m')); DECLARE CONTINUE HANDLER FORNOT FOUND SET done =TRUE; OPEN cur; read_loop: LOOP FETCH cur INTO partition_name; IF done THEN LEAVE read_loop; END IF; SET@sql= CONCAT('ALTER TABLE system_logs DROP PARTITION ', partition_name); PREPARE stmt FROM@sql; EXECUTE stmt; DEALLOCATEPREPARE stmt; END LOOP; CLOSE cur; END// DELIMITER ;
六、分区表性能优化
#
6.1 选择合适的分区数
建议分区数:10-100
过多分区增加管理开销
过少分区失去分区意义
#
6.2 确保查询能裁剪
-- 能裁剪(直接比较分区键) WHERE create_time >='2024-01-01'AND create_time <'2024-02-01'