2009-11-23 58 views
12

我有一个表看起来像这样创建新的分区:可从MySQL的事件调度

CREATE TABLE `Calls` (
    `calendar_id` int(11) NOT NULL, 
    `db_date` timestamp NOT NULL, 
    `cgn` varchar(32) DEFAULT NULL, 
    `cpn` varchar(32) DEFAULT NULL, 
    PRIMARY KEY (`calendar_id`), 
    KEY `db_date_idx` (`db_date`) 
) 
PARTITION BY RANGE (calendar_id)(
    PARTITION p20091024 VALUES LESS THAN (20091024) , 
    PARTITION p20091025 VALUES LESS THAN (20091025)); 

我可以以某种方式使用mysql调度自动添加一个新的分区(提前2天) - 我在寻找,将每天增加一个新的分区的例子 - 它会运行类似

alter table Calls add partition (partition p20091026 values less than(20091026)); 

凡p20091026/20091026在建造时计划任务运行,从现在开始+ 2日获得价值。 (或者我更好的通过cron编写脚本?)

+1

最多有每个表允许1024个分区,因此该方案将在3岁以下用完分区。而日常分区会提高性能的情况将非常罕见......如果你真的坚持这样做,你可能不需要每天创建一个新的分区,请参见[这里](http://stackoverflow.com/a/6163679/238419) – 2013-06-03 20:29:35

回答

28

是的,你可以这样做。

请注意,默认情况下,调度程序未处于活动状态(请参阅Event Scheduler Configuration),因此它不是零风险选项。例如,如果您的操作团队将您的应用程序迁移到新服务器,但忘记启用调度程序,则您的应用程序将变得流畅。还需要特殊权限,可能需要在新服务器上再次设置。我的建议:首先,创建一个处理定期分区维护的存储过程(请参阅下面的代码示例):如果表变得太大,则丢弃旧分区,并添加足够多的新分区(例如1周),以便即使维护过程不运行一段时间,你的应用程序不会死。

然后冗余调度该存储过程。使用MySQL调度程序,使用cron作业,并使用其他任何你喜欢的方式。那么如果一个调度程序不工作,另一个可以收拾松弛。如果你正确设计了sproc,如果它不需要做任何事情,那么执行no-op应该很便宜。您甚至可能想从您的应用中调用它,例如作为生成长时间运行报告时的第一条语句,或作为日常ETL过程(如果有的话)的一部分。我的观点是,计划任务的致命弱点是确保调度程序实际工作 - 所以在这里考虑冗余。

只要确保不在同一时间安排所有的电话,这样他们就不会踩到彼此! :-)

下面是您的维护过程的代码示例 - 首先修剪旧分区,然后添加新分区。我留下错误检查,并防止多个同时执行作为读者的exerise。

DELIMITER $$ 

DROP PROCEDURE IF EXISTS `test`.`UpdatePartitions` $$ 
CREATE PROCEDURE `test`.`UpdatePartitions`() 
BEGIN 

    DECLARE maxpart_date date; 
    DECLARE partition_count int; 
    DECLARE minpart date; 
    DECLARE droppart_sql date; 
    DECLARE newpart_date date; 
    DECLARE newpart_sql varchar(500); 

    SELECT COUNT(*) 
    INTO partition_count 
    FROM INFORMATION_SCHEMA.PARTITIONS 
    WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test'; 

    -- first, deal with pruning old partitions 
    -- TODO: set your desired # of partitions below, or make it parameterizable 
    WHILE (partition_count > 1000) 
    DO 

    -- optionally, do something here to deal with the parition you're dropping, e.g. 
    -- copy the data into an archive table 

    SELECT MIN(PARTITION_DESCRIPTION) 
     INTO minpart 
     FROM INFORMATION_SCHEMA.PARTITIONS 
     WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test'; 

    SET @sql := CONCAT('ALTER TABLE Calls DROP PARTITION p' 
         , CAST((minpart+0) as char(8)) 
         , ';'); 

    PREPARE stmt FROM @sql; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 

    SELECT COUNT(*) 
     INTO partition_count 
     FROM INFORMATION_SCHEMA.PARTITIONS 
     WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test'; 


    END WHILE; 

    SELECT MAX(PARTITION_DESCRIPTION) 
    INTO maxpart_date 
    FROM INFORMATION_SCHEMA.PARTITIONS 
    WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test'; 

    -- create enough partitions for at least the next week 
    WHILE (maxpart_date < CURDATE() + INTERVAL 7 DAY) 
    DO 

    SET newpart_date := maxpart_date + INTERVAL 1 DAY; 
    SET @sql := CONCAT('ALTER TABLE Calls ADD PARTITION (PARTITION p' 
         , CAST((newpart_date+0) as char(8)) 
         , ' values less than(' 
         , CAST((newpart_date+0) as char(8)) 
         , '));'); 

    PREPARE stmt FROM @sql; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 

    SELECT MAX(PARTITION_DESCRIPTION) 
     INTO maxpart_date 
     FROM INFORMATION_SCHEMA.PARTITIONS 
     WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test'; 

    END WHILE; 

END $$ 

DELIMITER ; 

BTW,分区维护(确保新的分区提前创建,修剪旧分区等),恕我直言,非常重要的自动化。我亲眼目睹一家大型企业数据仓库出现了一天的故障,因为最初创造了一年的分区,但是一旦明年出现,就没有人记得创建更多的分区。所以你在考虑自动化的时候非常好 - 这对你正在开发的项目来说是一个好兆头。 :-)

+0

当更改表格时,为什么不定义要修改的分区或即将丢失的东西。例如,它是如何知道将分区添加到'calender_Id'还是只能有一种类型的分区,并且由于分区已经创建,它默认为'calender_id' – 2010-08-15 21:12:40

+0

@shahmir - 上面的代码isn' t修改分区,它将删除旧分区并添加一个新分区。每个表只有一个分区方案。原始海报的问题显示分区发生在calendar_id上。 – 2010-08-18 17:59:03

8

贾斯汀那里的优秀解决方案。我把他的代码作为我当前项目的起点,并且想提一下我在实施时遇到的一些问题。

  1. 在你不应该包括MAXVALUE类型分区上运行此表中现有的分区结构 - 所有分区都必须由字面日期分隔。这是因为SELECT MAX(PARTITION_DESCRIPTION)将返回'MAXVALUE',它无法在下一步中转换为日期。如果您在调用过程时收到奇怪的信息,例如:“<”的非法组合排序,这可能是问题。

  2. 当从INFORMATION_SCHEMA表中选择分区名称时,添加“AND TABLE_SCHEMA ='dbname'”是一个好主意,因为同一个表可以存在多个分区(在不同的数据库中) ,它们都一起列在INFORMATION_SCHEMA表中。如果没有TABLE_SCHEMA规范,请选择例如。 MAX(PARTITION_DESCRIPTION)将为每个数据库中该名称的表的每个现有分区提供最大分区名称。

  3. 一路上,我与ALTER TABLE问题XXX ADD PARTITION,因为它是在贾斯汀的解决方案,我认为这是对分区名称(年月日)相同的格式被用作分隔符,其预计某处yyyy-mm-dd(v5.6.2)。

  4. 默认行为是仅在将来根据需要添加分区。如果您想为过去创建分区,则需要先为比您想要的最旧分区早的日期设置分区。例如。如果您保留过去30天的数据,请首先在35天前添加一个分区,然后运行该过程。当然,在空桌上这样做可能只是可行的,但我认为这值得一提。

  5. 为了在4中创建所需的过去/未来分区跨度,您最初需要运行两次该过程。对于上面4.中的示例,第一次运行将创建-35天的分区以及必要的未来分区。然后第二次运行将修剪-35和-30之间的分区。

这是我目前正在使用的。我添加了一些参数,使其从调用者的角度来看更加灵活。您可以指定数据库,表格,当前日期以及过去和未来要保留多少个分区。

我也改变分区的命名,这样命名p20110527分区代表日起从2011-5-27 00:00,而不是在那个时候结束一天

目前仍没有错误检查或预防中同时执行的:-)

DELIMITER $$ 

DROP PROCEDURE IF EXISTS UpdatePartitions $$ 

-- Procedure to delete old partitions and create new ones based on a given date. 
-- partitions older than (today_date - days_past) will be dropped 
-- enough new partitions will be made to cover until (today_date + days_future) 
CREATE PROCEDURE UpdatePartitions (dbname TEXT, tblname TEXT, today_date DATE, days_past INT, days_future INT) 
BEGIN 

DECLARE maxpart_date date; 
DECLARE partition_count int; 
DECLARE minpart date; 
DECLARE droppart_sql date; 
DECLARE newpart_date date; 
DECLARE newpart_sql varchar(500); 

SELECT COUNT(*) 
INTO partition_count 
FROM INFORMATION_SCHEMA.PARTITIONS 
WHERE TABLE_NAME=tblname 
AND TABLE_SCHEMA=dbname; 

-- SELECT partition_count; 

-- first, deal with pruning old partitions 
WHILE (partition_count > days_past + days_future) 
DO 
-- optionally, do something here to deal with the parition you're dropping, e.g. 
-- copy the data into an archive table 

SELECT STR_TO_DATE(MIN(PARTITION_DESCRIPTION), '''%Y-%m-%d''') 
    INTO minpart 
    FROM INFORMATION_SCHEMA.PARTITIONS 
    WHERE TABLE_NAME=tblname 
    AND TABLE_SCHEMA=dbname; 

-- SELECT minpart; 

SET @sql := CONCAT('ALTER TABLE ' 
        , tblname 
        , ' DROP PARTITION p' 
        , CAST(((minpart - INTERVAL 1 DAY)+0) as char(8)) 
        , ';'); 

-- SELECT @sql; 
PREPARE stmt FROM @sql; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 

SELECT COUNT(*) 
    INTO partition_count 
    FROM INFORMATION_SCHEMA.PARTITIONS 
    WHERE TABLE_NAME=tblname 
    AND TABLE_SCHEMA=dbname; 

-- SELECT partition_count; 

END WHILE; 

SELECT STR_TO_DATE(MAX(PARTITION_DESCRIPTION), '''%Y-%m-%d''') 
INTO maxpart_date 
FROM INFORMATION_SCHEMA.PARTITIONS 
WHERE TABLE_NAME=tblname 
AND TABLE_SCHEMA=dbname; 

-- select maxpart_date; 
-- create enough partitions for at least the next days_future days 
WHILE (maxpart_date < today_date + INTERVAL days_future DAY) 
DO 

-- select 'here1'; 
SET newpart_date := maxpart_date + INTERVAL 1 DAY; 
SET @sql := CONCAT('ALTER TABLE ' 
        , tblname 
        , ' ADD PARTITION (PARTITION p' 
        , CAST(((newpart_date - INTERVAL 1 DAY)+0) as char(8)) 
        , ' VALUES LESS THAN (''' 
        , newpart_date 
        , '''));'); 

-- SELECT @sql; 
PREPARE stmt FROM @sql; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 

SELECT STR_TO_DATE(MAX(PARTITION_DESCRIPTION), '''%Y-%m-%d''') 
    INTO maxpart_date 
    FROM INFORMATION_SCHEMA.PARTITIONS 
    WHERE TABLE_NAME=tblname 
    AND TABLE_SCHEMA=dbname; 

SET maxpart_date := newpart_date; 

END WHILE; 

END $$ 

DELIMITER ;