1. 程式人生 > >使用Java程式一次分段讀取所有資料(如海量資料)並計數處理

使用Java程式一次分段讀取所有資料(如海量資料)並計數處理

前段時間遇到一個問題,很簡單就是定時任務刪除資料庫中三個月前的資料;無非就是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刪除
        <span style="color: #993300;"><strong>Long id</strong> = 0l;</span>
        <strong><span style="color: #008000;">String dynamics = "select id,team_id,create_time
 from g_team_dynamic where id > ? order by team_id asc,id desc limit 10000";</span></strong>
        <span style="color: #ff0000;">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--;
        }</span>
        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處理對效率的影響會更大;