MySQL可以从事件调度程序创建新分区

nos*_*nos 12 mysql partitioning

我有一张桌子看起来像这样:

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));
Run Code Online (Sandbox Code Playgroud)

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

alter table Calls add partition (partition p20091026 values less than(20091026));
Run Code Online (Sandbox Code Playgroud)

在计划任务运行时构建p20091026/20091026,从现在+ 2天获取值.(或者我最好通过cron编写脚本吗?)

Jus*_*ant 28

是的,你可以这样做.

请注意,默认情况下调度程序不处于活动状态(请参阅事件调度程序配置),因此它不是零风险选项.例如,如果您的运营团队将您的应用迁移到新服务器,但忘记启用调度程序,那么您的应用就会受到影响.还需要特殊权限,这可能需要在新服务器上进行设置.

我的建议:首先,创建一个存储过程(请参阅下面的代码示例),它处理定期分区维护:如果表太大,则删除旧分区,并添加足够的新分区(例如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 ;
Run Code Online (Sandbox Code Playgroud)

BTW,分区维护(确保提前创建新分区,修剪旧分区等),恕我直言,对自动化至关重要.我个人看到一个大型企业数据仓库停运了一天,因为最初创建了一年的分区,但没有人记得在明年到来之后创建更多分区.所以你在这里考虑自动化是非常好的 - 这对你正在进行的项目来说是个好兆头.:-)


ifo*_*e2d 8

Justin的优秀解决方案.我把他的代码作为我当前项目的起点,并想提一下我实现它时出现的一些事情.

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

  2. 从INFORMATION_SCHEMA表中选择分区名称时,最好添加:"AND TABLE_SCHEMA ='dbname'",因为虽然同一个表(在不同的数据库中)可以存在多个具有相同名称的分区,但它们都列出了在INFORMATION_SCHEMA表中.如果没有TABLE_SCHEMA规范,请选择例如.MAX(PARTITION_DESCRIPTION)将为每个数据库中该名称的表的每个现有分区提供最大分区名称.

  3. 在某些地方我遇到了问题,就像在Justin的解决方案中使用ALTER TABLE xxx ADD PARTITION一样,我认为分区名称(yyyymmdd)的相同格式被用作预期yyyy-mm-dd的分区分隔符(v5.6.2).

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

  5. 为了在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 ;
Run Code Online (Sandbox Code Playgroud)