MySQL可以从事件调度器创建新分区吗

MySQL可以从事件调度器创建新分区吗,mysql,partitioning,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 R

我有一张像这样的桌子:

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是在计划任务运行时构造的,从now+2天导出值。或者我更适合通过cron编写脚本?

是的,您可以这样做

请注意,计划程序在默认情况下不处于活动状态,因此它不是零风险选项。例如,如果您的操作团队将您的应用程序迁移到新服务器,但忘记启用调度程序,则您的应用程序将被关闭。还需要特殊权限,这可能需要在新服务器上设置

我的建议:首先,创建一个存储过程(参见下面的代码示例),该过程处理定期分区维护:如果表太大,则删除旧分区,并添加足够的新分区(例如1周),以便即使维护过程暂时不运行,您的应用程序也不会死掉

然后冗余地调度对该存储过程的调用。使用MySQL调度程序,使用cron作业,以及使用任何其他您喜欢的方式。然后,如果一个调度器不工作,另一个调度器可以处理空闲时间。如果正确地设计存储过程,如果不需要执行任何操作,那么执行no-op应该是便宜的。您甚至可能希望从应用程序调用它,例如,作为生成长时间运行的报告时的第一条语句,或者作为日常ETL过程的一部分(如果有)。我的观点是,计划任务的致命弱点是确保调度器实际工作——因此请考虑冗余

只需确保不要同时安排所有通话,这样他们就不会互相踩踏了!:-

下面是一个代码示例,说明您的维护过程可能是什么样子的-首先它修剪旧分区,然后添加新分区。我将错误检查和防止多个同时执行作为读者的练习

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 ;

顺便说一句,分区维护——确保提前创建新分区、修剪旧分区等等——对于自动化来说至关重要。我个人看到一个大型企业数据仓库宕机一天,因为一年的分区一开始就被填满了,但是没有人记得在下一年到来时创建更多的分区。因此,您在这里考虑自动化非常好——这对您正在进行的项目是个好兆头-

贾斯汀提供的绝佳解决方案。我将他的代码作为我当前项目的起点,并想提及在我实现它时出现的一些事情


运行此操作的表中的现有分区结构不应包括MAXVALUE类型的分区-所有分区都必须由文字日期分隔。这是因为SELECT MAXPARTITION_DESCRIPTION将返回“MAXVALUE”,该值在下一步中无法转换为日期。如果在调用过程时收到一条奇怪的消息,比如:在更改表时,非法混合了“的排序规则,为什么不定义要修改的分区或缺少某些内容?”。例如,它如何知道正在将分区添加到Calendar_Id,或者您只能有一种类型的分区,并且由于分区已经创建,所以默认为Calendar_id@shahmir-上面的代码不是修改分区,而是删除一个旧分区并添加一个新分区。每个表只有一个分区方案。原始海报的问题显示分区发生在calendar_id上。每个表最多允许1024个分区,因此此解决方案将在3年内用完分区。日常分区可以提高性能的情况将非常罕见。。。如果你真的坚持这样做,你可能不需要每天创建一个新的分区,看,如果你不确定最大值是多少,我会在那里留下一个MAXVALUE分区。否则,任何值大于最大分区值的插入都将失败,因为MySQL没有可以放入的分区。当您尝试选择MAXPARTITION\u DESCRIPTION时,只需在其中添加PARTITION\u DESCRIPTION“MAXVALUE”。您必须更改ADD分区以相应地重新组织分区。
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 ;