2009-11-23 14 views
12

Mam tabelę szuka coś takiego:MySQL można tworzyć nowe partycje z terminarza zdarzeń

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)); 

Czy mogę jakoś wykorzystać planującego mysql automatycznie dodać nową partycję (2 dni) - Czekam na przykład, że każdy dzień dodać nową partycję - by go uruchomić coś podobnego

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

Gdzie p20091026/20091026 jest wykonana, gdy zaplanowana trasa zadanie wyprowadzania wartości od teraz + 2 dzień . (A może lepiej napisać to przez crona?)

+1

Istnieje maksymalnie 1024 przegród pozwoliło na stole, więc to rozwiązanie nie zabraknie partycji w ciągu 3 lat. A przypadki, w których partycje dzienne poprawiłyby wydajność, będą dość rzadkie ... Jeśli naprawdę nalegasz na zrobienie tego, nie musisz tworzyć nowej partycji codziennie, zobacz [tutaj] (http://stackoverflow.com/a/6163679/238419) –

Odpowiedz

28

Tak, możesz to zrobić.

Należy zauważyć, że harmonogram nie jest domyślnie aktywny (patrz Event Scheduler Configuration), więc nie jest to opcja zerowego ryzyka. Na przykład, jeśli twój zespół operacyjny przeniesie twoją aplikację na nowy serwer, ale zapomni włączyć harmonogram, twoja aplikacja zostanie zablokowana. Potrzebne są również specjalne uprawnienia, które mogą wymagać ponownego ustawienia na nowym serwerze.

Moja rada: najpierw utwórz procedurę składowaną (patrz przykładowy kod poniżej), która obsługuje okresową konserwację partycji: usuwanie starych partycji, jeśli tabela staje się zbyt duża, i dodawanie wystarczającej liczby nowych partycji (np. 1 tydzień), aby nawet jeśli Proces konserwacji nie działa przez jakiś czas, twoja aplikacja nie umiera.

Następnie nadmiarowo planuj połączenia z tym zapisanym proc. Użyj harmonogramu MySQL, użyj zadania cron i użyj dowolnego innego sposobu. Jeśli jeden program planujący nie działa, drugi może zwolnić. Jeśli projektujesz sproc poprawnie, to powinno być tanie, aby wykonać operację, jeśli nie musisz nic robić. Możesz nawet chcieć połączyć się z nią z aplikacji, np. jako pierwsza instrukcja generowania długiego raportu lub jako część codziennego procesu ETL (jeśli taki posiadasz). Chodzi mi o to, że piętą achillesową zaplanowanych zadań jest zapewnienie, że program planujący rzeczywiście działa - pomyśl więc o nadmiarowości tutaj.

Upewnij się, że nie zaplanujesz wszystkich połączeń jednocześnie, aby się nie nawzajem kontaktować! :-)

Oto przykład kodu dla tego, jak powinien wyglądać twój proces konserwacji - najpierw usuwa stare partycje, a następnie dodaje nowe. Zostawiłem sprawdzanie błędów i zapobieganie wielu jednoczesnym egzekucjom jako egzerise dla czytelnika.

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, obsługę partycji (zapewnienie nowe partycje są tworzone z góry, przycinanie starych partycji, itd.) Jest, moim skromnym zdaniem, niezwykle ważne, aby zautomatyzować. Osobiście widziałem, jak duża hurtownia danych dla przedsiębiorstw zanika na jeden dzień, ponieważ początkowa wartość partycji została utworzona na początku roku, ale nikt nie pamiętał, aby utworzyć więcej partycji po nadchodzącym roku. Więc dobrze, że myślisz o automatyzacji tutaj - dobrze wróży to dla projektu, nad którym pracujesz. :-)

+0

Zmieniając tabelę, dlaczego nie definiujesz, którą partycję zmodyfikować lub im czegoś brakuje. Na przykład, skąd wiadomo, że dodaje partycję do 'calender_Id' lub jest to, że możesz mieć tylko jeden typ partycji i ponieważ partycja jest już utworzona, domyślnie jest to' calender_id' –

+0

@shahmir - powyższy kod isn ' t modyfikując partycje, upuszcza starą partycję i dodaje nową. istnieje tylko jeden schemat partycji na tabelę. oryginalne pytanie z plakatu pokazuje, że partycjonowanie odbywa się na calendar_id. –

8

Doskonałe rozwiązanie od Justina. Zrobiłem jego kod jako punkt wyjścia dla mojego obecnego projektu i chciałbym wspomnieć o kilku rzeczach, które pojawiły się podczas jego wdrażania.

  1. Istniejąca struktura partycji w tabeli uruchomić ten nie powinien zawierać partycję typu MAXVALUE - Wszystkie partycje muszą być rozdzielany według dat dosłowne. Wynika to z tego, że SELECT MAX (PARTITION_DESCRIPTION) zwróci wartość "MAXVALUE", która nie zostanie przekonwertowana na datę w następnym kroku. Jeśli otrzymasz dziwną wiadomość podczas wywoływania procedury, mówiąc coś w stylu: nielegalna mieszanka sortowania dla "<", może to być problem.

  2. Warto dodać: "AND TABLE_SCHEMA =" dbname "" przy wybieraniu nazw partycji z tabeli INFORMATION_SCHEMA, ponieważ podczas gdy istnieje więcej niż jedna partycja o tej samej nazwie dla tej samej tabeli (w różnych bazach danych) Wszystkie są wymienione razem w tabeli INFORMATION_SCHEMA. Bez specyfikacji TABLE_SCHEMA twój wybór np. MAX (PARTITION_DESCRIPTION) da ci maksymalną nazwę partycji spośród wszystkich istniejących partycji dla tabel o tej nazwie w każdej bazie danych.

  3. Gdzieś po drodze miałem problemy z ALTER TABLE xxx DODAJ partycji co jest w roztworze Justina, myślę, że był to ten sam format dla nazwy partycji (rrrrmmdd) był używany jako ogranicznik działową, która oczekiwanej rrrr-mm-dd (v5.6.2).

  4. Domyślnym działaniem jest w miarę potrzeby dodawanie partycji tylko w przyszłości. Jeśli chcesz utworzyć partycje z przeszłości, musisz najpierw skonfigurować partycję na starszą datę niż najstarsza. Na przykład. jeśli przechowujesz dane przez ostatnie 30 dni, najpierw dodaj partycję, powiedzmy, 35 dni temu, a następnie uruchom procedurę. To prawda, możliwe, że można to zrobić tylko na pustym stole, ale pomyślałem, że warto o tym wspomnieć.

  5. Aby utworzyć pożądany zakres wcześniejszych/przyszłych partycji, jak w kroku 4. najpierw należy uruchomić procedurę dwukrotnie. W powyższym przykładzie, pierwsze uruchomienie spowoduje utworzenie partycji na 35 dni do zaprezentowania i niezbędnych przyszłych partycji. Drugi przebieg będzie wówczas przycinał partycje w zakresie od -35 do -30.

Oto, czego używam w tej chwili. Dodałem kilka parametrów, aby uczynić go bardziej elastycznym z punktu widzenia dzwoniącego. Można określić bazę danych, tabelę, bieżącą datę i liczbę partycji, które należy zachować zarówno w przeszłości, jak iw przyszłości.

ja też zmieniła nazewnictwo partycji tak, że partycja o nazwie p20110527 reprezentuje dni począwszy od 27.05.2011 00:00 zamiast dnia kończącym w tym czasie.

Nadal nie ma sprawdzanie błędów lub zapobieganie jednoczesnego wykonywania :-)

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 ; 
Powiązane problemy