2009-11-23 17 views
12

estoy teniendo una mesa mirando algo como esto:MySQL crear nuevas particiones del planificador de eventos

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

¿hay algún modo utilizar el programador mysql para agregar automáticamente una nueva partición (2 días de antelación) - estoy buscando a un ejemplo que sería, todos los días añadir una nueva partición - que había corrido algo así como

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

Dónde p20091026/20091026 se construye cuando la ejecución de la tarea programada, derivar el valor a partir de ahora + 2 días . (¿O es mejor escribir esto a través de cron?)

+1

Hay un máximo de 1024 particiones permitidas por mesa, por lo que esta solución se quedarán sin particiones en menos de 3 años. Y los casos en que las particiones diarias mejorarían el rendimiento serán bastante raros ... Si realmente insiste en hacerlo, es posible que no necesite crear una nueva partición todos los días, consulte [aquí] (http://stackoverflow.com)./a/6163679/238419) –

Respuesta

28

Sí, puede hacerlo.

Tenga en cuenta que el planificador no está activo de forma predeterminada (consulte Event Scheduler Configuration), por lo que no es una opción de cero riesgo. Por ejemplo, si su equipo de operaciones migra su aplicación a un nuevo servidor, pero se olvida de habilitar el programador, su aplicación recibirá una manguera. También se necesitan privilegios especiales, que de nuevo pueden necesitar ser configurados en un nuevo servidor.

Mi consejo: primero, cree un procedimiento almacenado (vea el ejemplo del código a continuación) que maneja el mantenimiento periódico de la partición: descarta particiones viejas si la tabla es demasiado grande y agrega suficientes particiones nuevas (ej. 1 semana) El proceso de mantenimiento no se ejecuta por un tiempo, su aplicación no morirá.

Luego programe llamadas de forma redundante a ese proceso almacenado. Utiliza el programador MySQL, utiliza un trabajo cron y utiliza cualquier otra forma que desees. Entonces, si un planificador no funciona, el otro puede tomar la holgura. Si diseñas correctamente el sproc, sería barato ejecutar un no-operativo si no necesita hacer nada. Es posible que incluso quieras llamarlo desde tu aplicación, p. como la primera declaración al generar un informe de larga ejecución o como parte de su proceso diario de ETL (si tiene uno). Mi punto es que el talón de Aquiles de las tareas programadas es garantizar que el planificador esté realmente funcionando, así que piense en la redundancia aquí.

¡Solo asegúrese de no programar todas las llamadas al mismo tiempo para que no se pisen! :-)

Aquí hay un ejemplo de código para ver cómo se vería su proceso de mantenimiento: primero borra las particiones viejas y luego agrega las nuevas. Dejé la comprobación de errores y evité múltiples ejecuciones simultáneas como un ejercicio para el lector.

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 ; 

Por cierto, el mantenimiento de partición (asegurando nuevas particiones se crean de antemano, la poda de particiones antiguas, etc.) está, en mi humilde opinión, una importancia crítica para automatizar. Personalmente, he visto un gran almacén de datos de la empresa bajar por un día porque inicialmente se creó un año de particiones, pero nadie recordó que crearía más particiones una vez que llegara el año siguiente. Por lo tanto, es muy bueno que esté pensando en la automatización, es un buen augurio para el proyecto en el que está trabajando. :-)

+0

Al modificar la tabla, ¿por qué no estás definiendo qué partición modificar o me falta algo? Por ejemplo, ¿cómo sabe que está agregando la partición a 'calender_Id' o es que solo puede tener un tipo de partición y porque la partición ya está creada, por defecto es' calender_id' –

+0

@shahmir - el código anterior no es ' t modificando particiones, está descartando una partición antigua y agregando una nueva. solo hay un esquema de partición por mesa. la pregunta del cartel original muestra que la partición ocurre en calendar_id. –

8

Excelente solución de Justin allí. Tomé su código como punto de partida para mi proyecto actual y me gustaría mencionar algunas cosas que surgieron mientras lo estaba implementando.

  1. La estructura de la partición existente en la tabla de ejecutar este no debe incluir una partición de tipo MAXVALUE - todas las particiones deben estar delimitados por fechas literales. Esto se debe a que SELECT MAX (PARTITION_DESCRIPTION) devolverá 'MAXVALUE' que no se puede convertir a una fecha en el próximo paso. Si recibe un mensaje extraño al llamar al procedimiento diciendo algo como: mezcla ilegal de colaciones para '<', este podría ser el problema.

  2. Es una buena idea agregar: "AND TABLE_SCHEMA = 'nombrebd'" al seleccionar nombres de partición de la tabla INFORMATION_SCHEMA, porque mientras más de una partición puede existir con el mismo nombre para la misma tabla (en diferentes bases de datos) , todos están listados en la tabla INFORMATION_SCHEMA juntos. Sin la especificación TABLE_SCHEMA, seleccione, por ej. MAX (PARTITION_DESCRIPTION) le dará el nombre máximo de la partición entre todas las particiones existentes para las tablas de ese nombre en cada base de datos.

  3. En algún momento tuve problemas con ALTER TABLE xxx ADD PARTITION como en la solución de Justin, creo que fue el mismo formato para el nombre de la partición (yyyymmdd) que se utilizaba como el delimitador de partición que se esperaba aaaa-mm-dd (v5.6.2).

  4. El comportamiento predeterminado es solo agregar particiones en el futuro según sea necesario. Si desea crear particiones para el pasado, primero deberá configurar una partición para una fecha más antigua que la más antigua que desee. P.ej. Si mantiene los datos de los últimos 30 días, primero agregue una partición para decir, hace 35 días y luego ejecute el procedimiento. De acuerdo, puede que solo sea factible hacer esto en una mesa vacía, pero pensé que valía la pena mencionarlo.

  5. Para crear la extensión deseada de particiones pasadas/futuras como en 4. inicialmente deberá ejecutar el procedimiento dos veces. Para el ejemplo en 4. anterior, la primera ejecución creará particiones por -35 días para presentar, y las particiones futuras necesarias. La segunda ejecución cortará las particiones entre -35 y -30 de distancia.

Esto es lo que estoy usando en este momento. Agregué algunos parámetros para hacerlo un poco más flexible desde el punto de vista de la persona que llama. Puede especificar la base de datos, la tabla, la fecha actual y la cantidad de particiones que se mantendrán tanto para el pasado como para el futuro.

También alteró la denominación de separaciones de modo que la partición llamada p20110527 representa el día a partir de 27/05/2011 00:00 en lugar del día terminando en ese momento.

Todavía hay ninguna comprobación de errores o la prevención de la ejecución simultánea :-)

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 ; 
Cuestiones relacionadas