-- ============================================================ -- 日志表按月分区统一管理(幂等迁移脚本) -- 创建时间:2026-05-06 -- 说明:确保 log_collect_raw、log_collect_analysis、log_collect_cycle -- 三张日志表均按月分区,并统一存储过程管理 -- 执行前提:USE cnc_log; 已执行 01-init-schema.sql 和 03-collect-analysis-tables.sql -- ============================================================ USE cnc_log; -- ============================================================ -- 1. log_collect_raw 按月分区 -- 该表在 01-init-schema.sql 中已定义分区,此处确认分区存在 -- 分区键:request_time -- ============================================================ -- 检查是否已有分区,若无则重建(幂等) SET @has_partition := (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME IS NOT NULL); -- 如果表没有分区(旧表),则需要重建 -- 注意:如果表已有分区(从DDL创建),此步骤会跳过 SET @sql_rebuild := IF(@has_partition = 0, 'ALTER TABLE cnc_log.log_collect_raw PARTITION BY RANGE (TO_DAYS(request_time)) ( PARTITION p202604 VALUES LESS THAN (TO_DAYS(''2026-05-01'')), PARTITION p202605 VALUES LESS THAN (TO_DAYS(''2026-06-01'')), PARTITION p202606 VALUES LESS THAN (TO_DAYS(''2026-07-01'')), PARTITION p202607 VALUES LESS THAN (TO_DAYS(''2026-08-01'')), PARTITION p_future VALUES LESS THAN MAXVALUE )', 'SELECT ''log_collect_raw 已有分区,跳过'''); PREPARE stmt FROM @sql_rebuild; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- ============================================================ -- 2. log_collect_analysis 按月分区 -- 该表在 03-collect-analysis-tables.sql 中已定义分区 -- 分区键:analysis_time -- ============================================================ SET @has_partition_a := (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME IS NOT NULL); SET @sql_rebuild_a := IF(@has_partition_a = 0, 'ALTER TABLE cnc_log.log_collect_analysis PARTITION BY RANGE (TO_DAYS(analysis_time)) ( PARTITION p202605 VALUES LESS THAN (TO_DAYS(''2026-06-01'')), PARTITION p202606 VALUES LESS THAN (TO_DAYS(''2026-07-01'')), PARTITION p202607 VALUES LESS THAN (TO_DAYS(''2026-08-01'')), PARTITION p_future VALUES LESS THAN MAXVALUE )', 'SELECT ''log_collect_analysis 已有分区,跳过'''); PREPARE stmt FROM @sql_rebuild_a; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- ============================================================ -- 3. log_collect_cycle 按月分区 -- 该表在 03-collect-analysis-tables.sql 中已定义分区 -- 分区键:cycle_time -- ============================================================ SET @has_partition_c := (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME IS NOT NULL); SET @sql_rebuild_c := IF(@has_partition_c = 0, 'ALTER TABLE cnc_log.log_collect_cycle PARTITION BY RANGE (TO_DAYS(cycle_time)) ( PARTITION p202605 VALUES LESS THAN (TO_DAYS(''2026-06-01'')), PARTITION p202606 VALUES LESS THAN (TO_DAYS(''2026-07-01'')), PARTITION p202607 VALUES LESS THAN (TO_DAYS(''2026-08-01'')), PARTITION p_future VALUES LESS THAN MAXVALUE )', 'SELECT ''log_collect_cycle 已有分区,跳过'''); PREPARE stmt FROM @sql_rebuild_c; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- ============================================================ -- 4. 更新存储过程 sp_ensure_partitions:覆盖全部3张分区表 -- ============================================================ DROP PROCEDURE IF EXISTS sp_ensure_partitions; DELIMITER $$ CREATE PROCEDURE sp_ensure_partitions() BEGIN -- 当前月的第一天 SET @base := DATE_FORMAT(CURDATE(), '%Y-%m-01'); SET @d1 := DATE_ADD(@base, INTERVAL 1 MONTH); SET @d2 := DATE_ADD(@base, INTERVAL 2 MONTH); SET @p1 := CONCAT('p', DATE_FORMAT(@d1, '%Y%m')); SET @p2 := CONCAT('p', DATE_FORMAT(@d2, '%Y%m')); -- ============================ -- log_collect_raw(分区键:request_time) -- ============================ IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p1) THEN SET @v1 := DATE_FORMAT(@d1, '%Y-%m-01'); SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_raw ADD PARTITION (PARTITION ', @p1, ' VALUES LESS THAN (TO_DAYS(', '''', @v1, '''', '))'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_raw', @p1, @v1); END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p2) THEN SET @v2 := DATE_FORMAT(@d2, '%Y-%m-01'); SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_raw ADD PARTITION (PARTITION ', @p2, ' VALUES LESS THAN (TO_DAYS(', '''', @v2, '''', '))'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_raw', @p2, @v2); END IF; -- ============================ -- log_collect_analysis(分区键:analysis_time) -- ============================ IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p1) THEN SET @v1 := DATE_FORMAT(@d1, '%Y-%m-01'); SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_analysis ADD PARTITION (PARTITION ', @p1, ' VALUES LESS THAN (TO_DAYS(', '''', @v1, '''', '))'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_analysis', @p1, @v1); END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p2) THEN SET @v2 := DATE_FORMAT(@d2, '%Y-%m-01'); SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_analysis ADD PARTITION (PARTITION ', @p2, ' VALUES LESS THAN (TO_DAYS(', '''', @v2, '''', '))'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_analysis', @p2, @v2); END IF; -- ============================ -- log_collect_cycle(分区键:cycle_time) -- ============================ IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p1) THEN SET @v1 := DATE_FORMAT(@d1, '%Y-%m-01'); SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_cycle ADD PARTITION (PARTITION ', @p1, ' VALUES LESS THAN (TO_DAYS(', '''', @v1, '''', '))'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_cycle', @p1, @v1); END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p2) THEN SET @v2 := DATE_FORMAT(@d2, '%Y-%m-01'); SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_cycle ADD PARTITION (PARTITION ', @p2, ' VALUES LESS THAN (TO_DAYS(', '''', @v2, '''', '))'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_cycle', @p2, @v2); END IF; END$$ DELIMITER ; -- ============================================================ -- 5. 更新 sp_check_partitions:覆盖全部3张分区表 -- ============================================================ DROP PROCEDURE IF EXISTS sp_check_partitions; DELIMITER $$ CREATE PROCEDURE sp_check_partitions() BEGIN SET @base := DATE_FORMAT(CURDATE(), '%Y-%m-01'); SET @d1 := DATE_ADD(@base, INTERVAL 1 MONTH); SET @d2 := DATE_ADD(@base, INTERVAL 2 MONTH); SET @p1 := CONCAT('p', DATE_FORMAT(@d1, '%Y%m')); SET @p2 := CONCAT('p', DATE_FORMAT(@d2, '%Y%m')); SET @need := 0; -- log_collect_raw IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p1) = 0 THEN SET @need = 1; END IF; IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p2) = 0 THEN SET @need = 1; END IF; -- log_collect_analysis IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p1) = 0 THEN SET @need = 1; END IF; IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p2) = 0 THEN SET @need = 1; END IF; -- log_collect_cycle IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p1) = 0 THEN SET @need = 1; END IF; IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p2) = 0 THEN SET @need = 1; END IF; IF @need = 1 THEN CALL sp_ensure_partitions(); END IF; SELECT @need AS need_partition_creation; END$$ DELIMITER ; -- ============================================================ -- 6. 确保分区追踪表存在 -- ============================================================ CREATE TABLE IF NOT EXISTS log_partition_tracker ( table_name VARCHAR(100) NOT NULL, partition_name VARCHAR(50) NOT NULL, partition_value VARCHAR(30) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (table_name, partition_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分区管理追踪表'; -- ============================================================ -- 7. 立即执行一次分区确保 -- ============================================================ CALL sp_ensure_partitions(); -- ============================================================ -- 8. 更新 MariaDB 事件:每月1日凌晨2:00执行 -- ============================================================ SET GLOBAL event_scheduler = ON; DROP EVENT IF EXISTS ev_ensure_partitions; CREATE EVENT IF NOT EXISTS ev_ensure_partitions ON SCHEDULE EVERY 1 MONTH STARTS TIMESTAMP(DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01 02:00:00')) DO CALL sp_check_partitions();