使用MySQL的過程,經常會遇到一個問題,比如說某張”log”表,用於保存某種記錄,隨着時間的不斷的累積數據,但是只有最新的一段時間的數據是有用的;這個時候會遇到性能和容量的瓶頸,需要將表中的歷史數據進行歸檔。
下面描述一種典型的做法:
比如說表結構如下:
CREATE TABLE `history` ( `id` int(11) NOT NULL, `value` text, `addtime` timestamp default current_timestamp, PRIMARY KEY (`id`), index idx_addtime(`addtime`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
這張表中保存有2012年2013年兩年的數據,現在需要將2012年的數據備份歸檔起來,但是2013年年初的數據還需要被查詢,因此不能簡單的進行如下的動作:
create table history_tmp like history; rename table history to history_2012,history_tmp to history;
需要在新表中保留2013年年初的數據,可以參照下面的流程進行:
create table history_tmp like history; maxid=select max(id) from history; minid=select id from history where addtime>"2013-01-01 00:00" order by addtime asc limit 1; last=0; set autocommit=1; for(i=minid;i<maxid+1000;i+=1000) { insert into history_tmp select * from history where id>=last and id<i lock in share mode; last=i; } begin; lock table history_tmp write,history write; maxid=select max(id) from history; insert into history_tmp select * from history where id>=last and id<=maxid; alter table history rename to history_2012; alter table history_tmp rename to history; unlock tables; commit;
說明:
- 使用alter table xx rename to xx,而不是rename是因為mysql的一個bug, bug地址,直接rename會出現”ERROR 1192 (HY000): Can’t execute the given command because you have active locked tables or an active transaction”錯誤.
- 需要使用lock history write來防止新的寫入。
- 這個方式是假設這個表在有插入和查詢操作,如果有update、delete操作可以通過類似OSC的算法使用trigger來實現。
- 不能直接使用insert select where id>minid這種方式,因為這樣會導致slave的延遲,而且遲遲不能提交的事務會導致undo log無法purge。