2009-11-23 51 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 ;