2009-11-23 3 views
12

Je vais avoir une table à la recherche quelque chose comme ceci:MySQL peut créer de nouvelles partitions du planificateur d'événements

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

Puis-je utiliser en quelque sorte le planificateur mysql pour ajouter automatiquement une nouvelle partition (2 jours à l'avance) - Je cherche un exemple qui, chaque jour ajouter une nouvelle partition - il avait couru quelque chose comme

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

Où p20091026/20091026 est construit lorsque l'exécution de la tâche planifiée, détermination de la valeur à partir de maintenant + 2 jours . (Ou est-ce que je suis meilleur de scripting cela par cron?)

+1

Il y a un maximum de 1024 partitions autorisées par table, donc cette solution à court de partitions en moins de 3 ans. Et les cas où les partitions quotidiennes amélioreraient les performances vont être plutôt rares ... Si vous insistez vraiment pour cela, vous n'aurez pas besoin de créer une nouvelle partition tous les jours, voir [ici] (http://stackoverflow.com)/a/6163679/238419) –

Répondre

28

Oui, vous pouvez le faire.

Notez que le planificateur n'est pas actif par défaut (voir Event Scheduler Configuration). Il ne s'agit donc pas d'une option à risque zéro. Par exemple, si votre équipe d'opérations migre votre application vers un nouveau serveur, mais oublie d'activer le planificateur, votre application sera arrosée. Des privilèges spéciaux sont également nécessaires, ce qui peut nécessiter une nouvelle configuration sur un nouveau serveur. Mon conseil: d'abord, créer une procédure stockée (voir exemple de code ci-dessous) qui gère la maintenance périodique des partitions: abandonner les anciennes partitions si la table devient trop grande et ajouter suffisamment de nouvelles partitions (par exemple 1 semaine) proc de maintenance n'est pas exécuté pendant un certain temps, votre application ne mourra pas.

Ensuite, planifiez de manière redondante les appels à ce proc stocké. Utilisez le planificateur MySQL, utilisez un travail cron et utilisez n'importe quel autre moyen. Ensuite, si un planificateur ne fonctionne pas, l'autre peut prendre le relais. Si vous concevez le sproc correctement, il devrait être bon marché d'exécuter un no-op s'il n'a pas besoin de faire quoi que ce soit. Vous pourriez même vouloir l'appeler depuis votre application, par ex. comme première déclaration lors de la génération d'un rapport de longue durée ou dans le cadre de votre processus ETL quotidien (si vous en avez un). Mon point est que le talon d'Achille des tâches planifiées est de s'assurer que le planificateur fonctionne réellement - alors pensez à la redondance ici. Assurez-vous de ne pas programmer tous les appels en même temps afin qu'ils ne se chevauchent pas! :-)

Voici un exemple de code pour ce à quoi pourrait ressembler votre proc de maintenance - d'abord, il élague les anciennes partitions, puis en ajoute de nouvelles. J'ai laissé la vérification d'erreur et empêché plusieurs exécutions simultanées en tant qu'exercice pour le lecteur.

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, la maintenance de la partition (assurant de nouvelles partitions sont créées à l'avance, la taille des partitions anciennes, etc.) est, à mon humble avis, d'une importance cruciale pour automatiser. J'ai personnellement vu un entrepôt de données d'une grande entreprise disparaître pendant une journée, car une partition d'une année avait déjà été créée, mais personne ne s'est souvenu de créer plus de partitions une fois l'année suivante. Donc, c'est très bien que vous pensiez à l'automatisation ici - c'est de bon augure pour le projet sur lequel vous travaillez. :-)

+0

Lors de la modification de la table, pourquoi ne pas définir quelle partition modifier ou im manquer quelque chose. Par exemple comment sait-il que l'ajout de la partition à 'calender_Id' ou est-ce que vous ne pouvez avoir qu'un type de partition et parce que la partition est déjà créée, par défaut' calender_id' –

+0

@shahmir - le code ci-dessus isn ' En modifiant les partitions, il supprime une ancienne partition et en ajoute une nouvelle. il n'y a qu'un seul schéma de partition par table. la question de l'auteur d'origine montre que le partitionnement a lieu sur calendar_id. –

8

Excellente solution de Justin là-bas. J'ai pris son code comme point de départ pour mon projet actuel et je voudrais mentionner quelques éléments qui ont été soulevés pendant que je l'ai implémenté.

  1. La structure de partition existante dans le tableau que vous exécutez ce ne devrait pas inclure sur une partition de type MAXVALUE - toutes les partitions doivent être délimitées par des dates littérales. C'est parce que SELECT MAX (PARTITION_DESCRIPTION) retournera 'MAXVALUE' qui ne sera pas converti en date à l'étape suivante. Si vous recevez un message étrange lorsque vous appelez la procédure en disant quelque chose comme: mélange illégal de classements pour '<', cela pourrait être le problème.C'est une bonne idée d'ajouter: "AND TABLE_SCHEMA = 'nombdd'" lors de la sélection des noms de partition de la table INFORMATION_SCHEMA, car il peut exister plusieurs partitions portant le même nom pour la même table (dans différentes bases de données) , ils sont tous répertoriés dans la table INFORMATION_SCHEMA ensemble. Sans la spécification TABLE_SCHEMA, sélectionnez par exemple. MAX (PARTITION_DESCRIPTION) vous donnera le nom de partition maximum entre chaque partition existante pour les tables de ce nom dans chaque base de données. Quelque part le long du chemin j'ai eu des problèmes avec ALTER TABLE xxx ADD PARTITION comme dans la solution de Justin, je pense que c'était le même format pour le nom de partition (yyyymmdd) qui était utilisé comme délimiteur de partition aaaa-mm-jj (v5.6.2).

  2. Le comportement par défaut est d'ajouter que des partitions à l'avenir si nécessaire. Si vous souhaitez créer des partitions pour le passé, vous devez d'abord configurer une partition pour une date plus ancienne que la partition la plus ancienne que vous souhaitez. Par exemple. Si vous conservez des données pour les 30 derniers jours, ajoutez d'abord une partition par exemple, il y a 35 jours, puis exécutez la procédure. Certes, il ne sera peut-être possible de le faire que sur une table vide, mais je pense qu'il vaut la peine de le mentionner.

  3. Afin de créer la durée souhaitée des partitions futures/passé comme 4. vous devrez d'abord pour exécuter la procédure deux fois. Pour l'exemple en 4. ci-dessus, la première exécution va créer des partitions pour -35 jours à présenter, et les futures partitions nécessaires. La deuxième exécution va ensuite couper les partitions entre -35 et -30.

Voici ce que j'utilise pour le moment. J'ai ajouté quelques paramètres pour le rendre un peu plus flexible du point de vue de l'appelant. Vous pouvez spécifier la base de données, la table, la date actuelle et le nombre de partitions à conserver pour le passé et le futur.

J'ai aussi modifié la dénomination des partitions afin que la partition nommée p20110527 représente le jour à partir de 27.05.2011 00:00 au lieu du jour la fin à ce moment-là.

Il n'y a toujours pas de vérification d'erreur ou la prévention de l'exécution simultanée :-)

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 ; 
Questions connexes