-- 创建存储过程【通过数据库和表名】建立【partition_number】get分区,分区间隔为【gaps】
-- datasource 数据库名称
-- table_name 数据库表名
-- partition_number 新建分区的数量
-- partition_type 分区类型(0-按天分区,1-按月分区,2-按年分区)
-- gaps 分区间隔(按分区类型分别为gaps天、gaps月、gaps年)
-- max_p_num 最大分区数
-- 此存储过程执行的前提是执行的表是有分区的表
DELIMITER $$
DROP PROCEDURE IF EXISTS `auto_add_partitions`$$
CREATE PROCEDURE `auto_add_partitions`(
in datasource varchar(50),
in table_name varchar(50),
in partition_number int,
in partition_type int,
in gaps int,
in max_p_num int
)
L_END:
BEGIN
declare max_p_name varchar(50);
declare next_partition_name varchar(50);
declare min_p_name varchar(50);
declare cur_date_str varchar(20);
declare next_date_str varchar(20);
declare exec_sql varchar(300);
declare p_num int default 0;
declare i int default 1;
declare min_partition_description int;
-- 查询表是否是分区表,不是则结束
set p_num = (select count(1) from information_schema.PARTITIONS p where TABLE_NAME = table_name and TABLE_SCHEMA = datasource);
if p_num = 0 then
select concat( 'not partition with ', table_name);
leave L_END;
else
while (i <= partition_number) do
-- 最大分区
SET max_p_name=(select IFNULL(partition_name,'') from information_schema.PARTITIONS p where TABLE_NAME = table_name and TABLE_SCHEMA = datasource order by PARTITION_DESCRIPTION desc limit 1);
if max_p_name = NULL then
select concat('no partitions on ', table_name);
leave L_END;
end if;
if max_p_name = '' then
select concat('no partitions on ', table_name);
leave L_END;
end if;
-- 按日分区
if partition_type = 0 then
SET cur_date_str = (select cast(DATE_SUB(STR_TO_DATE(substr(max_p_name, 2, length(max_p_name)) ,'%Y%m%d'),INTERVAL -1*gaps day) as char) from dual);
set next_date_str = (select cast(DATE_SUB(STR_TO_DATE(replace(cur_date_str, '-', '') ,'%Y%m%d'),INTERVAL -1*gaps day) as char) from dual);
SET next_partition_name= concat('p',replace(cur_date_str, '-', ''));
set @exec_sql = concat('alter table ', datasource, '.', table_name,' add partition (partition ', next_partition_name, ' values less than (to_days(''', next_date_str, ''')));');
prepare stmt from @exec_sql;
execute stmt;
deallocate prepare stmt;
end if;
-- 按月分区
if partition_type = 1 then
SET cur_date_str = (select cast(DATE_SUB(STR_TO_DATE(substr(max_p_name, 2, length(max_p_name)) ,'%Y%m'),INTERVAL -1*gaps month) as char) from dual);
set next_date_str = (select cast(DATE_SUB(STR_TO_DATE(replace(cur_date_str, '-', '') ,'%Y%m'),INTERVAL -1*gaps month) as char) from dual);
SET next_partition_name= concat('p',replace(cur_date_str, '-', ''));
set @exec_sql = concat('alter table ', datasource, '.', table_name,' add partition (partition ', next_partition_name, ' values less than (to_days(''', next_date_str, ''')));');
prepare stmt from @exec_sql;
execute stmt;
deallocate prepare stmt;
end if;
-- 按年分区
if partition_type = 2 then
SET cur_date_str = (select cast(DATE_SUB(STR_TO_DATE(substr(max_p_name, 2, length(max_p_name)) ,'%Y'),INTERVAL -1*gaps year) as char) from dual);
set next_date_str = (select cast(DATE_SUB(STR_TO_DATE(replace(cur_date_str, '-', '') ,'%Y'),INTERVAL -1*gaps year) as char) from dual);
SET next_partition_name= concat('p',replace(cur_date_str, '-', ''));
set @exec_sql = concat('alter table ', datasource, '.', table_name,' add partition (partition ', next_partition_name, ' values less than (to_days(''', next_date_str, ''')));');
prepare stmt from @exec_sql;
execute stmt;
deallocate prepare stmt;
end if;
commit;
set i = (i + 1);
end while;
set @delFlag = 1;
while @delFlag > 0 do
-- 查询当前分区数
select count(PARTITION_NAME) into p_num from information_schema.PARTITIONS p where TABLE_NAME = table_name and TABLE_SCHEMA = datasource;
if p_num <= max_p_num then
set @delFlag = 0;
end if;
if p_num > max_p_num then
-- 最小分区
set min_partition_description = (select min(PARTITION_DESCRIPTION) from information_schema.PARTITIONS p where TABLE_NAME = table_name and TABLE_SCHEMA = datasource);
set min_p_name = (select partition_name from information_schema.PARTITIONS p where TABLE_NAME = table_name and TABLE_SCHEMA = datasource and PARTITION_DESCRIPTION = min_partition_description);
set @exec_sql = concat('alter table ', datasource, '.', table_name,' drop partition ', min_p_name, ';');
prepare stmt from @exec_sql;
execute stmt;
deallocate prepare stmt;
end if;
end while;
end if;
END$$
DELIMITER ;
执行此存储过程有两个方式,以下使用的是MySql的事件调度器(另一种是通过代码执行call脚本实现)
首先,确保事件调度器(Event Scheduler)已经开启
SHOW VARIABLES LIKE 'event_scheduler';
如果返回的值是OFF,则可以通过以下命令开启事件调度器:
SET GLOBAL event_scheduler = ON;
然后新建一个事件
-- 对sys_log表的 create_time 字段设置分区条件为按日分区
-- 创建一个Event,每天执行一次,同时最多保存365天的日志数据
DELIMITER $$
DROP EVENT IF EXISTS `sys_log_auto_partition`$$
CREATE EVENT `sys_log_auto_partition`
ON SCHEDULE EVERY 1 DAY ON COMPLETION PRESERVE
ENABLE
DO
L_END:
BEGIN
declare partition_number int default 1;
declare max_p_name varchar(50);
-- 最大分区
SET max_p_name=(select IFNULL(partition_name,'') from information_schema.PARTITIONS p where TABLE_NAME = 'sys_log' and TABLE_SCHEMA = 'mock' order by PARTITION_DESCRIPTION desc limit 1);
if max_p_name = NULL then
select concat('no partitions on ', table_name);
leave L_END;
end if;
if max_p_name = '' then
select concat('no partitions on ', table_name);
leave L_END;
end if;
-- 两个日期之间的差
SET partition_number = (select DATEDIFF(DATE_FORMAT(now(),'%Y%m%d'), substr(max_p_name, 2, length(max_p_name))) from dual);
if partition_number > 0 then
call auto_add_partitions('mock', 'sys_log', partition_number, 0, 1, 365);
end if;
END$$
DELIMITER ;
-- 注意事项:
-- 对于Mysql 5.1以上版本来说,表分区的索引字段必须是主键
-- 存储过程中,DECLARE 必须紧跟着BEGIN,否则会报看不懂的错误
-- 游标的DECLARE需要在定义声明之后,否则会报错
-- 如果是自己安装的Mysql,有可能Event功能是未开启的,在创建Event时会提示错误;修改my.cnf,在 [mysqld] 下添加event_scheduler=1后重启即可。
-- 执行时,需要选中DELIMITER $$到DELIMITER ;
相关表
CREATE TABLE `sys_log` (
`log_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`case_name` varchar(300) DEFAULT NULL COMMENT '案例名称',
`env_ip` varchar(39) DEFAULT NULL COMMENT '环境IP',
`service_scene` varchar(20) DEFAULT NULL COMMENT '服务码+场景码',
`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
`response_format` varchar(10) DEFAULT NULL COMMENT '响应格式',
`request_ip` varchar(39) DEFAULT NULL COMMENT '请求IP(最长可存IPv6:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX)',
`check_key` varchar(300) DEFAULT NULL COMMENT '校验键',
`check_value` varchar(300) DEFAULT NULL COMMENT '校验值',
`response_content` longtext COMMENT '响应报文',
`request_content` longtext COMMENT '请求报文',
`del_flag` int(1) DEFAULT '0' COMMENT '删除标识,0-未删除;1-已删除',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(300) DEFAULT NULL COMMENT '备注',
`ser_no` varchar(64) DEFAULT NULL COMMENT '接口流水号',
`response_id` bigint(20) DEFAULT NULL COMMENT '报文响应表主键',
`hit_target` int(1) DEFAULT NULL COMMENT '命中接口,0-未命中;1-命中',
PRIMARY KEY (`log_id`,`create_time`),
KEY `sys_log_case_name_IDX` (`case_name`) USING BTREE,
KEY `sys_log_env_ip_IDX` (`env_ip`) USING BTREE,
KEY `sys_log_service_scene_IDX` (`service_scene`) USING BTREE,
KEY `sys_log_check_key_IDX` (`check_key`) USING BTREE,
KEY `sys_log_check_value_IDX` (`check_value`) USING BTREE,
KEY `sys_log_ser_no_IDX` (`ser_no`) USING BTREE,
KEY `sys_log_response_id_IDX` (`response_id`) USING BTREE,
KEY `sys_log_hit_target_IDX` (`hit_target`) USING BTREE
) COMMENT='系统接口请求日志表'
PARTITION BY RANGE(to_days(`create_time`)) (
partition p20250101 values less than (to_days('2025-01-01'))
);