前段時間遇到一個問題,很簡單就是定時任務刪除數據庫中三個月前的數據;無非就是delete...from...where;當時的需求要考慮這幾個問題:
1.效率
2.一次讀取全部
3.保留部分數據
先說一下當時的需求,刪除三個月前的動態(團隊動態),但有些團隊的動態本身就很少,刪除了影響前台數據量,所以只刪除三個月內動態>200條的數據;
其實完全可以一個簡單的delete from,count計數,但這對磁盤IO消耗很大;所以要在內存中處理;如何讀取數據,計數是主要問題;
下面是當時實現代碼
//定時刪除團隊動態
public class AutoDelTeamDynamic {
protected final static Log logger = LogFactory.getLog(AutoDelTeamDynamic.class);
public void executeInternal() {
logger.warn(" autoDelTeamDynamic start! ");
Date d = new Date();//當前時間
Date startDate = DateUtil.getDateInDayAgo(d, -90);
Date delDate = DateUtil.dateUtil2date(startDate, "yyyy-MM-dd");//三個月前
//三個月團隊內動態數量大於200刪除
Long id = 0l;
String dynamics = "select id,team_id,create_time from g_team_dynamic where id > ? order by team_id asc,id desc limit 10000";
while(true){
List<Map<String, Object>> result = Db.executeQuery(dynamics, new Object[]{id},false);
if(result == null || result.size() < 10000){
break;
}
Map<Long,Integer> map = new HashMap<Long, Integer>();
StringBuffer sb = new StringBuffer();
for(Map<String, Object> numMap : result){
id = (Long)numMap.get("id");
Long team_id = (Long)numMap.get("team_id");
Date create_time = (Date)numMap.get("create_time");
Date date = DateUtil.dateUtil2date(create_time, "yyyy-MM-dd");
if(DateUtil.compareTwoDate(date, delDate) == 1){
if(map.containsKey(team_id)){
map.put(team_id, map.get(team_id) + 1);
if(map.get(team_id) > 200){
sb.append(id).append(",");
}
}else{
map.put(team_id, 1);
}
}
}
if(sb.length() > 0){
sb.deleteCharAt(sb.length()-1);
delDynamic(sb.toString());
}
id--;
}
logger.warn(" autoDelTeamDynamic end! ");
}
private void delDynamic(String ids){
String delsql = " DELETE FROM g_team_dynamic WHERE id in ("+ids+")";
int a = Db.executeUpdate(delsql,null, false);
if(a <= 0){
logger.error("刪除每日任務記錄失敗!"+delsql);
}
}
}
以上代碼是工程中的,只是一個思路問題;SQL語句是分段讀取數據的,如何讓分段讀取能夠讀取到數據庫中全部數據,這里用到了while(true),每次讀取10000條,即只要數據庫中數據還多於一萬就一直向下執行,直到數據少於10000時結束;還有個問題就是分段讀取數據的銜接,注意Long id=0,及SQL語句中的where條件,主鍵id之后被數據中讀取數據重新賦值,一次循環執行SQL結束,id--,這樣就可以將兩次Limit的數據銜接上(id主鍵自增),如此執行,就可以一次讀取到數據庫中所有數據;
下面就是計數問題,在這里是使用Map計數的,每當發現一個team_id,判斷Map中是否包含,不包含創建,計數1;包含則在原來個數上加一,如此計數,當team_id對應數據大於200,直接將數據庫中記錄id放入可刪除字符串中;
最后就是根據主鍵刪除記錄,如此做是效率最高的刪除操作,where條件放入in條件,一次傳入所有需要刪除記錄的ID;
這樣就可以實現需求;海量數據處理確實很難模擬,不在互聯網公司很難接觸到,一個刪除操作就要如此麻煩,但我們都應該知道,內存可以擴展,但IO處理對效率的影響會更大;
