You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haoliang-net/database/sqls/07-log-tables-partition.sql

217 lines
11 KiB
SQL

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

-- ============================================================
-- 日志表按月分区统一管理(幂等迁移脚本)
-- 创建时间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();