zoukankan      html  css  js  c++  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删除
    		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处理对效率的影响会更大;



  • 相关阅读:
    5-1
    浅谈sql中的in与not in,exists与not exists的区别
    理解SQL SERVER中的分区表
    SQLSERVER SQL性能优化
    SQL Server Profiler使用方法
    SQL Server中的三种Join方式
    执行计划
    执行计划sql
    INSERT INTO SELECT
    设计模式学习笔记-单例模式
  • 原文地址:https://www.cnblogs.com/wufengxyz/p/2302499.html
Copyright © 2011-2022 走看看