近日,由於部門數據庫讀庫空間過小,提出刪除掉兩個月之前日志表的分區(數據庫分區是按時間月分區),記述如下:
上網搜索資料發現刪除表分區大概分這么幾步:
1、查詢需要刪除掉的分區:
select t.DATAPARTITIONNAME from syscat.datapartitions t where tabname = '?' with ur
syscat.datapartitions表存放所有的分區,根據表名時間查詢出所要刪除的分區名。
2、detach分區到一張臨時表(該操作會創建臨時表,臨時表已存在會報錯),detach是分離分區的意思:
alter table 表名 detach partition 分區名 into table 臨時表名;
3、最后再drop掉臨時表。
然后我就代碼:
1)據時間、表名查出要刪除的分區名,存在list;
2)遍歷這個list,循環中先detach分區,再刪除臨時表。
PS:問題來了,測試中發現drop臨時表的時候會報錯,報該臨時表有分區數據再往里面遷,這里我就懷疑detach操作是異步的了,上網一看相關資料,果不其然。
原來detach操作會先將syscat.datapartitions表中的分區改名,但是所屬表名不會改,然后將status狀態打為L,表明該分區正在遷移中。
於是我代碼先根據表名判斷存不存在status為L的數據,沒有再進行刪表,本地測試成功,批量刪除分區成功。然而一放到測試環境,測試人員反饋說報錯,又是刪表出錯,這時候奇怪了,不是判斷過狀態L了嗎,而且本地是可以的,糾結了好久!!!最后想通了,detach既然是異步的,那么打上這個L狀態也是異步的了,還沒打我后續代碼就開心的來判斷L狀態進行刪表了。。。幹!!!那搞這個狀態有個屁用。
后面我給了兩種方案:
1、設置休眠 Thread.sleep(),由於這個時間不好把控,而且我們庫表都是有很多分表的,粗粗估計了下,要刪除的分區大概是3張總表*72張分表*40個分區,每個停個100毫秒就耗時很大了,故此方案pass。(不考慮用線程並發,因為DBA不允許。。)
2、遞歸刪表 直至成功
public void drop(表名){
try{
刪表操作
}catch(Exception e){
drop();
}
}
其實第二種方法是可行的,沒辦法,代碼評審被否掉,於是給了第三種方案:
3、搞兩個定時任務
1)先detach分區 分離到拼接的臨時表:tmp+表名+分區名,然后新建一張表,存這些被分離的臨時表名字,這張表有三個字段:臨時表名、狀態(刪除,未刪除)、更新時間
2)然后過段時間,去做刪除臨時表操作,刪完將臨刪除狀態更新為已刪除,做了個查詢功能監控待刪除這張表。