從MYSQL 數據庫歸檔 到 歸檔設計

到數據歸檔,很多人的第一個概念就是,不就是無用的數據,換個地方放嗎,直接拷貝,刪除不就得了,有那麼麻煩。

我見到過的,聽到過的數據庫歸檔的方法有以下幾種

1  數據通過人工的手段來進行清理,直接將表換名字,然後在重建一個新的表,承接數據。

首先這樣的做法一個字,快,這是這樣做法的好處所在,但另一方面要考慮的問題就是,業務要不要停,涉及的人有多少,如果光是IT 的還好說,但恰恰這樣做,絕對不會光光牽扯 IT, 業務的人一定是要牽扯進來,然後就是各種流程和通知,要在幾點幾點,某個業務,甚至整體業務暫時停止。

2  數據通過MYSQL dump 或者其他的備份方式,將數據備份出來,在將數據恢復到數據歸檔庫中,然後將備份的數據直接手動清理掉,這樣的做法速度也很快,對業務的影響也比較小,基本上可以算是透明的方式了,但還是避免不了人工的介入,並且也不可能是天天這樣做。

3  數據通過工具的方式來進行處理,例如pt-archiver 的方式來進行數據的歸檔和清理,但這個工具貌似bug不少,pt-1126

4  自己設計數據歸檔

自己設計數據歸檔的面就廣了,有使用程序來做的,例如JAVA ,Python等等,也有使用存儲過程來進行的。

下面就是一個MYSQL 針對一個數據庫表歸檔的案例(這個案例也是有缺陷的,但目前是秉承着夠用就好,以及時間成本的原則)

首先設計一個歸檔要考慮的問題如下

 

1 歸檔表的大小,以及每日最大,或最小的歸檔數據量,或者數據過期時間

   同時歸檔表是否必須是全量的數據歸檔,還是可以拋棄一些數據,例如有一些日誌的歸檔中可能存在一些無用的數據,是否還必須全量的歸檔等等都是要考慮的問題,歸檔數據並不一定是原封不動的歸檔,有的邏輯上,只歸檔一些數據關鍵點也是可以的。

2 歸檔的數據量,數據歸檔一般根據上面的東西,歸檔有一次性歸檔,和規律有固定日期的歸檔,一次性的歸檔一般歸檔的數據量比較大,而有規律的歸檔則歸檔的數據量並不大,對比兩者的方式,其實定期歸檔(有規律)的要有優勢一些,主要是數據是不斷灌入的,而數據的歸檔如果也是不斷輸出的,這樣整體這個表的數據量就會有一個平衡,不會一下子少了很多,要不就是在清理的前一天,數據量已經大到一定的水平,有可能影響性能。

3 歸檔的方法,自己定義數據的歸檔方面,可以每次歸檔將數據灌入一個表,也可以定期的將數據寫入不同的歸檔表,例如已歸檔日期和後綴的方式來將每次寫入的數據進行分割,或者建立分區表的方式來進行歸檔。

4  歸檔的方式是否靈活,有的歸檔的方法僅僅針對一個表來進行歸檔,有的方法是可以靈活配置,可以任意擴展。那就都任意擴展,靈活配置不就好了,其實隨着能任意擴展或者靈活配置,則工作量就會變大,這也要考慮一個性價比,具體要考慮表的數量以及歸檔的方式。

下面就是一個簡單的例子,需求是一張表每天數據量在40- 50 萬,主要都是來自於客戶的短信以及消息發送的內容。表中的數據要保留半年之內的,其餘的數據可以移走。

以下以最簡單的自動化的方案來講

下圖是基於案例來講的

因爲數據庫是MYSQL 所以考慮了歸檔一次是多大的批量,避免歸檔數據量過大的時候將生產庫hang 死,另外配置表主要的功能是有兩個 1 限制一次拷貝和清理的數據量,2 控制拷貝過期數據的日期限制

下面是這段代碼,如果看的不方便,下面有截圖

DELIMITER $$

DROP PROCEDURE IF EXISTS  archive_data;

create PROCEDURE archive_data()

BEGIN

  declare row_s int;  #最大執行多少次每次1000條

  declare save_month tinyint;  #保留多少月之前的數據

  declare times int;  #執行次數記錄

  declare min_row_s int;  # 當前數據庫最小的tid

  declare archive_date datetime;

  select @times := 1;  #設置每天初始清理次數初始值 

  select @row_s := max_row_clean from db_archive.db_config order by id limit 1;  #獲取當前配置庫數據

  select @save_month := archive_save_date from db_archive.db_config order by id limit 1; 

  select @min_row_s := min(tid) from msgcdb.t_sms_message; #獲取當前系統最小的TID號

  select @max_row_s := max(tid) from msgcdb.t_sms_message; #獲取當前系統最大的TID號

  select @archive_date := DATE_SUB(CURDATE(), INTERVAL @save_month MONTH);

  select @row_s, @save_month,@archive_date,@min_row_s,@max_row_s;

  

    if @min_row_s = @max_row_s then 

    set @times = @row_s + 1;

    elseif @min_row_s is null then

    set @times = @row_s + 1;

    end if; 

    

   insert into db_archive.archive_log (save_month,times,min_row_s,max_row_s,archive_date,row_s,insert_time,delete_time,type_s) values (@save_month,@times,@min_row_s,@max_row_s,@archive_date,@row_s,sysdate(),sysdate(),'initial');

 

   select @times, @min_row_s;

     while @times < @row_s DO

        begin

insert into db_archive.t_sms_message (tid,summary_id,uid,code,channel,batch_id,done_time,phone,sms_content,create_time,send_time,storage_time,status,estimatedTime,operate_type,origin,creator_id ,dept_id,del_flag,priority,template_id,repetitions_num) 

        select tid,summary_id,uid,code,channel,batch_id,done_time,phone,sms_content,create_time,send_time,storage_time,status,estimatedTime,operate_type,origin,creator_id ,dept_id,del_flag,priority,template_id,repetitions_num 

        from msgcdb.t_sms_message 

        where tid >= @min_row_s and tid < @min_row_s + 1000 and status <> 0 and storage_time < @archive_date; 

set @times = @times + 1;

         insert into db_archive.archive_log (save_month,times,min_row_s,max_row_s,archive_date,row_s,insert_time,delete_time,type_s) values (@save_month,@times,@min_row_s,@max_row_s,@archive_date,@row_s,sysdate(),sysdate(),'inserted');

        

    delete from msgcdb.t_sms_message where tid >= @min_row_s and tid < @min_row_s + 1000 and status <> 0 and storage_time < @archive_date;

        insert into db_archive.archive_log (save_month,times,min_row_s,max_row_s,archive_date,row_s,insert_time,delete_time,type_s) values (@save_month,@times,@min_row_s,@max_row_s,@archive_date,@row_s,sysdate(),sysdate(),'deleted');

       

        select @min_row_s,@max_row_s;

        select @min_row_s := min(tid) from msgcdb.t_sms_message;

        select @max_row_s := max(tid) from msgcdb.t_sms_message;

        select @min_row_s,@max_row_s;

            if @min_row_s = @max_row_s then 

set @times = @row_s + 1;

            elseif @min_row_s is null then

            set @times = @row_s + 1;

            end if; 

           

        end;

     END WHILE;

END$$

DELIMITER ;

配置表

歸檔日誌表

爲什麼要這麼設計,其實尋根溯源有兩點

1 簡單有效,夠用原則

2 設計配置表的主要原因是對於非IT 人員,例如project manager 或者其他的人員,也可以調整歸檔的時間,例如 archive_save_date 的數字就是保留多少月的數據,max_row_clean,就是當前的數字 *1000 就是每天最大的歸檔數據量。通過這兩個參數雙重限制每天的歸檔的數據量,避免歸檔的時間太長,影響了備份,或其他操作。而日誌表本身就是一個查看歸檔成功失敗的東西,其中的type_s  就是表現數據歸檔操作狀態的東西,通過日誌表可以反映歸檔多少數據,每次操作消耗的時間,以及當前操作獲取的系統變量是什麼,方便出現故障時,查看到底歸檔的數據少不少,或者大致可能出現問題。

下面是這兩個表的結構

這樣歸檔有沒有缺點,當然有,缺點馬上就可以說出幾個

1 爲什麼還要在本地機歸檔數據,不應該是傳送到其他機器上嗎

2 爲什麼不設置每次歸檔的數量限制(每次限制操作的行數),這對MYSQL不是很用嗎,爲什麼要寫死。

3  爲什麼要用MYSQL 存儲過程來做,使用python不是更靈活

其實一言難盡,都和需求有關,所以很多設計出來的東西,外人一看一堆毛病,如果你進入到他的內部,一段時間估計你就懂得爲什麼會設計出這樣或那樣的東西。

最近有一句話挺時髦,資本根本不care你技術不技術,除非你做到行業NO.1,纔有可能翻個身。

羣裏有一些免費書,可自取

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章