1. 程式人生 > 資料庫 >記一次線上MySQL資料歸檔方案

記一次線上MySQL資料歸檔方案

由於線上的MySQL實時表資料量太大,即使建了索引查詢速度也不理想,上週下班前經理讓我對線上MySQL的七張源資料層面的實時表進行歸檔,現表僅保留近三天的資料,三天之前的資料全部歸檔到歷史表中

一、基本思想

考慮到按照時間進行歸檔,因此MySQL按時間建立分割槽表,並且動態維護每張歷史表的分割槽,將三天前的資料插入到歷史表中,根據時間的不同會落到不同的分割槽中;校驗資料量在沒有丟失的情況下刪除原表資料並記錄日誌。

二、前期準備

所謂磨刀不誤砍柴工,首先需要了解MySQL分割槽表的理論、如何建立分割槽表、如何動態維護分割槽表…

2.1 MySQL建立分割槽表

原表的欄位基本都是varchar

型別,為了方便分割槽建立歷史表的同時增加一個歸檔日期欄位,並按照該欄位進行分割槽給定一個初始分割槽

DROP TABLE IF EXISTS `aj_gaaj_archive `;
CREATE TABLE `aj_gaaj_archive `
(
    ...,
    `BACKUP_TIME` date comment '歸檔日期'
) PARTITION BY RANGE (to_days(`BACKUP_TIME`))(
        partition p20201224 values less than (to_days('20201225'))
        );

檢視分割槽表的分割槽情況

SELECT partition_name                   part,
       partition_expression             expr,
       partition_description            descr,
       FROM_DAYS(partition_description) lessthan_sendtime,
       table_rows
FROM INFORMATION_SCHEMA.partitions
WHERE TABLE_SCHEMA = SCHEMA()
  AND TABLE_NAME = 'aj_gaaj_archive';

2.2 MySQL分割槽表維護

使用過hive的都知道,hive是可以進行動態分割槽的,根據資料的不同動態的新增分割槽,據瞭解MySQL不支援這種功能,因此需要我們手動的去增加分割槽,從一位老哥的部落格中拔下一段方便維護分割槽表的儲存過程 []

create procedure auto_set_partitions(in databasename varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
                                     in tablename varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
                                     in partition_number int, in partitiontype int, in gaps int)
L_END:
begin
    declare max_partition_description varchar(255) default '';
    declare p_name varchar(255) default 0;
    declare p_description varchar(255) default 0;
    declare isexist_partition varchar(255) default 0;
    declare i int default 1;

    -- 檢視對應資料庫對應表是否已經有手動分割槽[自動分割槽前提是必須有手動分割槽]
    select partition_name
    into isexist_partition
    from information_schema.partitions
    where table_schema = databasename
      and table_name = tablename
    limit 1;
    -- 如果不存在則列印錯誤並退出儲存過程
    if isexist_partition <=> '' then
        select 'partition table not is exist' as "ERROR";
        leave L_END;
    end if;

    -- 獲取最大[降序獲取]的分割槽描述[值]
    select partition_description
    into max_partition_description
    from information_schema.partitions
    where table_schema = databasename
      and table_name = tablename
    order by partition_description desc
    limit 1;

    -- 如果最大分割槽沒有,說明沒有手動分割槽,則無法建立自動分割槽
    if max_partition_description <=> '' then
        select 'partition table is error' as "ERROR";
        leave L_END;
    end if;

    -- 替換前後的單引號[''兩個引號表示一個單引號的轉義]
    -- set max_partition_description = REPLACE(max_partition_description, '''', '');
    -- 或使用如下語句
    set max_partition_description = REPLACE(max_partition_description - 1, '\'', '');

    -- 自動建立number個分割槽
    while (i <= partition_number)
        do
            if (partitiontype = 0) then
                -- 每個分割槽按天遞增,遞增gaps天
                set p_description = DATE_ADD(FROM_DAYS(max_partition_description), interval i * gaps day);
            elseif (partitiontype = 1) then
                -- 每個分割槽按月遞增,遞增gaps月
                set p_description = DATE_ADD(FROM_DAYS(max_partition_description), interval i * gaps month);
            else
                -- 每個分割槽按年遞增,遞增gaps年
                set p_description = DATE_ADD(FROM_DAYS(max_partition_description), interval i * gaps year);
            end if;
            -- 刪除空格
            set p_name = REPLACE(p_description, ' ', '');
            -- 例如10.20的記錄實際是less than 10.21
            set p_description = DATE_ADD(p_description, interval 1 day);
            -- 如果有橫杆替換為空
            set p_name = REPLACE(p_name, '-', '');
            -- 刪除時間冒號
            set p_name = REPLACE(p_name, ':', '');
            -- alter table tablename add partition ( partition pname values less than ('2017-02-20 10:05:56') );
            set @sql = CONCAT('ALTER TABLE ', tablename, ' ADD PARTITION ( PARTITION p', p_name,
                              ' VALUES LESS THAN (TO_DAYS(\'', p_description, '\')))');
            -- set @sql=CONCAT('ALTER TABLE ', tablename ,' ADD PARTITION ( PARTITION p', p_name ,' VALUES LESS THAN (TO_DAYS(\'', p_description ,'\')))');
            -- 列印sql變數
            -- select @sql;
            -- 準備sql語句
            PREPARE stmt from @sql;
            -- 執行sql語句
            EXECUTE stmt;
            -- 釋放資源
            DEALLOCATE PREPARE stmt;
            -- 遞增變數
            set i = (i + 1);

        end while;
end;

三、本地實施

因為歸檔的表屬於ODS級別,不得不慎重啊,稍有不慎就要跑路的