Mysql數據按天分區,按期刪除

需求:mysql

  1.日誌表須要按天分區sql

  2.只保留一個月數據express

方案:ui

  1.建立兩個事件,一個事件生成將來須要的分區,另外一個事件按期檢查過時數據(移除分區)spa

  2.建立事件每小時執行一次,刪除事件天天執行一次日誌

  3.事件開始時須要先建立一個當前所需分區code

全量方法:orm

  1.先構造存儲過程 create_partition_today :將錶轉化爲分區表,並將歷史數據歸集到該分區,將來數據則按天放置:blog

#alter table to partition table
DELIMITER $$
USE `dc_log`$$
DROP PROCEDURE IF EXISTS `create_partition_today`$$
CREATE PROCEDURE `create_partition_today`(IN_SCHEMANAME VARCHAR(64), IN_TABLENAME VARCHAR(64))
  BEGIN
    DECLARE BEGINTIME TIMESTAMP;
    DECLARE ENDTIME TIMESTAMP;
    DECLARE DAYS_ENDTIME INT;
    DECLARE PARTITIONNAME VARCHAR(16);
    SET BEGINTIME = NOW();
    SET ENDTIME = BEGINTIME + INTERVAL 1 DAY;
    SET PARTITIONNAME = DATE_FORMAT(BEGINTIME, 'p%Y%m%d');
    SET DAYS_ENDTIME = TO_DAYS(ENDTIME);
    SET @SQL = CONCAT('ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
                        ' PARTITION BY RANGE (to_days(create_time))
    (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', DAYS_ENDTIME, '))');
    PREPARE STMT FROM @SQL;
    EXECUTE STMT;
    DEALLOCATE PREPARE STMT;
  END$$
DELIMITER ;

  2.按天構造分區的存儲過程create_partition_by_day:事件

#procedure of build partition of today and next day
DELIMITER $$
USE `dc_log`$$
DROP PROCEDURE IF EXISTS `create_partition_by_day`$$
CREATE PROCEDURE `create_partition_by_day`(IN_SCHEMANAME VARCHAR(64), IN_TABLENAME VARCHAR(64))
  BEGIN
    DECLARE ROWS_CNT INT UNSIGNED;
    DECLARE BEGINTIME TIMESTAMP;
    DECLARE ENDTIME TIMESTAMP;
    DECLARE DAYS_ENDTIME INT;
    DECLARE PARTITIONNAME VARCHAR(16);
    SET BEGINTIME = NOW() + INTERVAL 1 DAY;
    SET PARTITIONNAME = DATE_FORMAT(BEGINTIME, 'p%Y%m%d');
    SET ENDTIME = BEGINTIME + INTERVAL 1 DAY;
    SET DAYS_ENDTIME = TO_DAYS(ENDTIME);
    SELECT COUNT(*)
    INTO ROWS_CNT
    FROM information_schema.partitions
    WHERE table_schema = IN_SCHEMANAME AND table_name = IN_TABLENAME AND partition_name = PARTITIONNAME;
    IF ROWS_CNT = 0
    THEN
      SET @SQL = CONCAT('ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
                        ' ADD PARTITION (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', DAYS_ENDTIME, '))');
      PREPARE STMT FROM @SQL;
      EXECUTE STMT;
      DEALLOCATE PREPARE STMT;
    ELSE
      SELECT CONCAT("partition `", PARTITIONNAME, "` for table `", IN_SCHEMANAME, ".", IN_TABLENAME,
                    "` already exists") AS result;
    END IF;
  END$$
DELIMITER ;

  3.按天清除數據的存儲過程clear_partition_by_day

DELIMITER $$
USE `dc_log`$$
DROP PROCEDURE IF EXISTS `clear_partition_by_day`$$
CREATE PROCEDURE `clear_partition_by_day`(IN_SCHEMANAME VARCHAR(64), IN_TABLENAME VARCHAR(64))
  BEGIN
    DECLARE NOWDAYS INT;
    DECLARE Done INT;
    DECLARE part VARCHAR(64);
    DECLARE descr INT;
    DECLARE rs CURSOR FOR SELECT
                            partition_name        part,
                            partition_description descr
                          FROM information_schema.partitions
                          WHERE
                            table_schema = IN_SCHEMANAME
                            AND table_name = IN_TABLENAME;
    /* 異常處理 */
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET Done = 1;
    OPEN rs;
    SET NOWDAYS = TO_DAYS(NOW());
    FETCH rs into part,descr;
    while Done is null DO
      IF NOWDAYS - descr > 30
        THEN
          select descr AS cc;
          SET @SQL = CONCAT('ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
                            ' DROP PARTITION ', part);
          select descr AS aa;
          SELECT @SQL AS result;
          select descr AS bb;
          PREPARE STMT FROM @SQL;
          EXECUTE STMT;
          DEALLOCATE PREPARE STMT;
        END IF;
      FETCH rs into part,descr;
    end WHILE;
    CLOSE rs;
  END$$
DELIMITER ;

  注意:以上過程有可能報錯,這裏記錄兩個錯誤:

  1.分區字段必須包含主鍵:

  這裏採用的是事件字段做爲分區字段,固然不多是主鍵,因此再mysql中會報錯,查閱資料大多說是由於分區表中不能保證數據惟一,須要將分區健歸入主鍵纔可,不清楚原理.若是不肯意這樣作也能夠考慮,刪除已有主鍵,無主鍵的表能夠用任意字段做爲分區字段

ALTER TABLE dc_system.service_push DROP PRIMARY KEY;

  2.時間字段不容許做爲分區字段:

  這是由於 DATETIME 會受到時間區的影響,mysql裏面分區的時間字段須要用CURRENT_TIMESTAMP

ALTER TABLE business_log
  MODIFY create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;

4.繼續往下,打開事件變量,這裏直接修改

SET GLOBAL event_scheduler = 'ON'; #打開

  重啓後會被重置,若是須要永久修改,在my.cnf(mysql配置文件)中[mysqld]部分中添加下面內容,重啓MYSQL

event_scheduler=ON

5.調用存儲過程建立當天分區:

#created today's partition
CALL create_partition_today('dc_log', 'business_log');

6.建立事件:

DELIMITER $$
USE `dc_log`$$
CREATE EVENT IF NOT EXISTS `e_part_manage`
  ON SCHEDULE EVERY 1 HOUR #every minute
  STARTS '2018-05-01 18:27:00'
  ON COMPLETION PRESERVE
ENABLE
  COMMENT 'Creating partitions'
DO BEGIN
  CALL dc_log.create_partition_by_day('dc_log', 'business_log');
END$$
DELIMITER ;


#event of clear data which out of date of 30 days
DELIMITER $$
USE `dc_log`$$
CREATE EVENT IF NOT EXISTS `clear_data`
  ON SCHEDULE EVERY 1 DAY #every minute
  STARTS '2018-05-01 15:58:51'
  ON COMPLETION PRESERVE
ENABLE
  COMMENT 'clearing data'
DO BEGIN
  CALL clear_partition_by_day('dc_log', 'business_log');
END$$
DELIMITER ;

7.若是有其餘表也須要如此處理,則先執行建立當天分區,再修改事件:

#service_push #same steps of table business_log
ALTER TABLE dc_system.service_push DROP PRIMARY KEY;
CALL create_partition_today('dc_system', 'service_push');


DELIMITER ;
DELIMITER $$
ALTER EVENT e_part_manage
DO BEGIN
  CALL dc_log.create_partition_by_day('dc_log', 'business_log');
  CALL dc_log.create_partition_by_day('dc_system', 'service_push');
END$$
DELIMITER ;

DELIMITER $$
ALTER EVENT clear_data
DO BEGIN
  CALL dc_log.clear_partition_by_day('dc_log', 'business_log') ;
  CALL dc_log.clear_partition_by_day('dc_system', 'service_push');
END$$
DELIMITER ;

8.查看錶的分區狀況:

use dc_log;
SELECT
      partition_name part,
      partition_expression expr,
      partition_description descr,
      table_rows
FROM
      INFORMATION_SCHEMA.partitions
WHERE
      TABLE_SCHEMA = SCHEMA()
AND TABLE_NAME='service_push' ;

9.手動添加分區:

ALTER TABLE dc_log.all_log ADD PARTITION (PARTITION 'PARTITIONNAME' VALUES LESS THAN (TO_DAYS(now())));

10.查看事件和事件開關

show events;
SHOW VARIABLES LIKE 'event_scheduler';

done

相關文章
相關標籤/搜索