数据库中有多张表要做分区表(按日期分区),但是这些表用于分区的日期字段记录形式并不统一,有如下三种类型:
PARTITION par_20151225 VALUES LESS THAN (736323) ENGINE = InnoDB PARTITION par_20151010 VALUES LESS THAN (1444492800) ENGINE = InnoDB PARTITION par_20151006 VALUES LESS THAN (1452480) ENGINE = InnoDB
首先创建一个元数据信息表,用于记录要分区的表以及分区类型,如下:
mysql> select * from op_tb_partition; +-------------------------------+----------+ | tb_name | timetype | +-------------------------------+----------+ | NL_APP_ACTION_ERROR_TRACE | unixtime | | NL_APP_ACTION_SQL_TRACE | unixtime | | NL_APP_ACTION_TRACE | unixtime | | NL_MOB_APP_CRASH_DATA | usertime | | NL_MOB_APP_INTERACTION_TRACE | unixtime | | NL_NET_MOB_PAGE_DATA | unixtime | | NL_NET_MOB_PING_DATA | unixtime | | NL_NET_PAGE_DATA | unixtime | | NL_NET_PING_DATA | unixtime | | NL_MOB_APP_ERROR_TRACE | datetime | | NL_MOB_APP_SOCKET_ERROR_TRACE | datetime | +-------------------------------+----------+ 11 rows in set (0.00 sec)
此外,还需创建一张日志表,用于记录存储过程执行的详细信息,如下:
mysql> desc change_partition_log; +------------+---------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------------+------+-----+-------------------+-----------------------------+ | tm_base | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | | op_type | varchar(20) | YES | | NULL | | | change_sql | varchar(2000) | YES | | NULL | | +------------+---------------+------+-----+-------------------+-----------------------------+ 3 rows in set (0.00 sec)
为了是维护工作变的简单一些,希望能用一个存储过程来实现所有类型表的分区维护,因此编写了如下的存储过程:
DELIMITER $$ DROP PROCEDURE IF EXISTS lens_mobapp_trace.add_partition_original; CREATE PROCEDURE `add_partition_original`() BEGIN DECLARE table_name varchar(255) DEFAULT ""; DECLARE tmp_sql varchar(255) DEFAULT ""; DECLARE max_day_will int DEFAULT 0; DECLARE max_day_now int DEFAULT 0; DECLARE difference int DEFAULT 0; DECLARE i int DEFAULT 1; DECLARE tmp_day_value int DEFAULT 0; DECLARE tmp_day int DEFAULT 1; DECLARE alter_sql varchar(255) DEFAULT ""; DECLARE par_name varchar(255) DEFAULT ""; DECLARE done int DEFAULT - 1; DECLARE myCursor CURSOR FOR SELECT tb_name FROM op_tb_partition; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; SET @tmp_sql = CONCAT("select to_days(date_add(now(),interval 6 day)) into @max_day_will"); PREPARE stmt FROM @tmp_sql; EXECUTE stmt; OPEN myCursor; myLoop: LOOP FETCH myCursor INTO table_name; IF done = 1 THEN LEAVE myLoop; END IF; set @tmp_sql = concat("SELECT timetype FROM op_tb_partition WHERE tb_name = '", table_name, "' into @timetype"); prepare stmt from @tmp_sql; execute stmt; case when @timetype="datetime" then set @tmp_sql = concat("SELECT max(substring(partition_name,5)) FROM INFORMATION_SCHEMA.partitions WHERE TABLE_NAME = '", table_name, "' and TABLE_SCHEMA ='lens_mobapp_trace' into @max_day_now"); prepare stmt from @tmp_sql; execute stmt; set @tmp_sql = concat("select @max_day_will - to_days('",@max_day_now,"') into @difference"); prepare stmt from @tmp_sql; execute stmt; while i <= @difference do set @tmp_sql = concat("select date_format(date_add('",@max_day_now,"' ,interval ",i," day), '%Y%m%d') into @tmp_day"); prepare stmt from @tmp_sql; execute stmt; set @tmp_sql = concat("select to_days(date_format(date_add('",@max_day_now,"' ,interval 1+",i," day), '%Y%m%d')) into @tmp_day_value"); prepare stmt from @tmp_sql; execute stmt; set @par_name = concat("par_",@tmp_day); SET @alter_sql = CONCAT("alter table ", table_name, " add partition(partition ", @par_name, " values less than (", @tmp_day_value, "))"); insert into change_partition_log values( now(), "add_trace", @alter_sql); prepare stmt from @alter_sql; execute stmt; set i = i+1; end while; set i = 1; when @timetype="usertime" then set @tmp_sql = concat("SELECT max(substring(partition_name,5)) FROM INFORMATION_SCHEMA.partitions WHERE TABLE_NAME = '", table_name, "' and TABLE_SCHEMA ='lens_mobapp_trace' into @max_day_now"); prepare stmt from @tmp_sql; execute stmt; set @tmp_sql = concat("select @max_day_will - to_days('",@max_day_now,"') into @difference"); prepare stmt from @tmp_sql; execute stmt; while i <= @difference do set @tmp_sql = concat("select date_format(date_add('",@max_day_now,"' ,interval ",i," day), '%Y%m%d') into @tmp_day"); prepare stmt from @tmp_sql; execute stmt; set @tmp_sql = concat("select nl_to_timestamp(date_format(date_add('",@max_day_now,"' ,interval 1+",i," day), '%Y%m%d')) into @tmp_day_value"); prepare stmt from @tmp_sql; execute stmt; set @par_name = concat("par_",@tmp_day); SET @alter_sql = CONCAT("alter table ", table_name, " add partition(partition ", @par_name, " values less than (", @tmp_day_value, "))"); insert into change_partition_log values( now(),"add_trace" , @alter_sql); prepare stmt from @alter_sql; execute stmt; set i = i+1; end while; set i = 1; when @timetype="unixtime" then set @tmp_sql = concat("SELECT max(substring(partition_name,5)) FROM INFORMATION_SCHEMA.partitions WHERE TABLE_NAME = '", table_name, "' and TABLE_SCHEMA ='lens_mobapp_trace' into @max_day_now"); prepare stmt from @tmp_sql; execute stmt; set @tmp_sql = concat("select @max_day_will - to_days('",@max_day_now,"') into @difference"); prepare stmt from @tmp_sql; execute stmt; while i <= @difference do set @tmp_sql = concat("select date_format(date_add('",@max_day_now,"' ,interval ",i," day), '%Y%m%d') into @tmp_day"); prepare stmt from @tmp_sql; execute stmt; set @tmp_sql = concat("select unix_timestamp(date_format(date_add('",@max_day_now,"' ,interval 1+",i," day), '%Y%m%d')) into @tmp_day_value"); prepare stmt from @tmp_sql; execute stmt; set @par_name = concat("par_",@tmp_day); SET @alter_sql = CONCAT("alter table ", table_name, " add partition(partition ", @par_name, " values less than (", @tmp_day_value, "))"); insert into change_partition_log values( now(),"add_trace" , @alter_sql); prepare stmt from @alter_sql; execute stmt; set i = i+1; end while; set i = 1; end case; END LOOP myLoop; CLOSE myCursor; END$$ DELIMITER ;
由于业务需求,这些表的数据只需要保留3个月,再次编写删除分区的存储过程,如下:
DELIMITER $$ DROP PROCEDURE IF EXISTS lens_mobapp_trace.del_partition_original; CREATE PROCEDURE `del_partition_original`() BEGIN declare table_name varchar(255) default 0; declare tmp_sql varchar(255) default ""; declare max_par_now varchar(255) default ""; declare par_del_num varchar(255) default ""; declare par_del varchar(255) default ""; declare alter_sql varchar(255) default ""; declare i int default 0; declare done int default -1; declare myCursor cursor for select tb_name from op_tb_partition; declare continue handler for not found set done = 1; open myCursor; myLoop:LOOP fetch myCursor into table_name; if done = 1 then leave myLoop; end if; set @tmp_sql = concat("select count(*) FROM INFORMATION_SCHEMA.partitions WHERE TABLE_NAME = '",table_name,"' and TABLE_SCHEMA ='lens_mobapp_trace' into @max_par_now"); prepare stmt from @tmp_sql; execute stmt; set @par_del_num = @max_par_now-90-7; while i < @par_del_num do set @tmp_sql = concat("select partition_name from INFORMATION_SCHEMA.partitions where TABLE_SCHEMA ='lens_mobapp_trace' and table_name='",table_name,"' order by partition_name limit 1 into @par_del"); prepare stmt from @tmp_sql; execute stmt; set @alter_sql = concat("alter table ",table_name," drop partition ",@par_del); prepare stmt from @alter_sql; execute stmt; insert into change_partition_log values(now(),"drop_trace", concat(i,"--",@par_del,"--",@alter_sql)); set i = i+1; end while; set i = 0; end loop myLoop; close myCursor; END$$ DELIMITER ;
最后编写定期执行上述存储过程的调度event,如下:
DELIMITER ;; CREATE EVENT lens_mobapp_trace.`manager_partition_original` ON SCHEDULE EVERY 1 DAY STARTS '2013-12-24 00:30:00' ON COMPLETION PRESERVE ENABLE DO BEGIN call add_partition_original; call del_partition_original; END ;; DELIMITER ;