如何优化大数据量的方法

2019/04/19 22:42
阅读数 49

随着项目上线,业务数据会越来越多。这个时候,很多开发时适用的方法、任务,在庞大数据量面前就会变得很不堪,经常会出现超时,慢查询,异常等等问题。

所以,一般在开发阶段,我们怎么能避免这些问题呢?一切皆有套路。

一般处理套路:
SQL优化,数据库加索引,多线程,并行计算,异步处理,大事务拆小事务,缓存,数据异构等等。

今天分享一个通过时间维度优化SQL的方法。说白了,就是如何将时间拆小。

还是直接贴代码:

开始版本,按天拆分时间:

/**
     * 按给定的天数切割时间段
     * @param startDate
     * @param endDate
     * @param amount 按多少天切割
     * @return
     * @throws ParseException
     */
    public static List<TimeSlot> splitTimeSlot(Date startDate, Date endDate, Integer amount) throws ParseException {
        Calendar canlandar1 = Calendar.getInstance();//开始时间
        Calendar canlandar2 = Calendar.getInstance();//结束时间
        canlandar1.setTime(com.midea.ec.fc.impl.utils.DateUtils.getDateStartTime(startDate));
        canlandar2.setTime(com.midea.ec.fc.impl.utils.DateUtils.getDateStartTime(endDate));
        List<TimeSlot> returnList = new ArrayList<TimeSlot>();
        while(canlandar1.compareTo(canlandar2) < 1){
            TimeSlot timeSlot = new TimeSlot();
            Date start = canlandar1.getTime();
            canlandar1.add(Calendar.DATE, amount);//每次循环增加amount天
            Date end = canlandar1.getTime();
            timeSlot.setStartDate(DateTool.getDateTime(start));
            timeSlot.setEndDate(DateTool.getDateTime(end.before(canlandar2.getTime()) ? end : canlandar2.getTime()));
            returnList.add(timeSlot);
            timeSlot.setStartDateTime(start);
            timeSlot.setEndDateTime(end.before(canlandar2.getTime()) ? end : canlandar2.getTime());
        }
        return returnList;
    }

发现某些天数据量大跑不动了,然后继续拆分成小时:

public static List<TimeSlot> splitTimeSlotByHour(String startDate, String endDate) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = format.parse(startDate);
        Date date2 = format.parse(endDate);

        Calendar canlandar1 = Calendar.getInstance();//开始时间
        Calendar canlandar2 = Calendar.getInstance();//结束时间
        canlandar1.setTime(date1);//2016-11-01
        canlandar2.setTime(date2);//2016-11-11
        List<TimeSlot> returnList = new ArrayList<TimeSlot>();
        while(canlandar1.compareTo(canlandar2) < 1){
            TimeSlot timeSlot = new TimeSlot();
            Date start = canlandar1.getTime();
            canlandar1.add(Calendar.HOUR, 1);//每次循环增加一天
            Date end = canlandar1.getTime();
            timeSlot.setStartDate(DateTool.getDateTime(start));
            timeSlot.setEndDate(DateTool.getDateTime(end));
            returnList.add(timeSlot);
            timeSlot.setStartDateTime(start);
            timeSlot.setEndDateTime(end);
        }
        if(CollectionUtils.isNotEmpty(returnList)) returnList.remove(returnList.size()-1);
        return returnList;
    }

最后发现光棍节的这天,小时也跑不动了,还要继续拆分钟?改成自己拆吧。

/**
 * @Auther: majx2
 * @Date: 2018-11-14 11:24
 * @Description:
 */
public class JobTimeoutHelper {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
// 时间区间拆分数量    
private int intervalCount = 3;
// 拆分最大层级
    private int maxCount = 3;

    private JobTimeoutHelper(){}
    public static JobTimeoutHelper create(){
        return new JobTimeoutHelper();
    }

    public void splitTime(Date startDate,Date endDate,String jobName,TimeSplitHandler handler){
        splitTime(startDate,endDate,0,jobName,handler);
    }

    public void splitTime(Date startDate,Date endDate,int level,String jobName,TimeSplitHandler handler){
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if(level>=maxCount){
            logger.info("{}超时,拆分已到上限,无法继续拆分:时间:{}",jobName,MessageFormat.format("开始时间:{0},结束时间:{1}",df.format(startDate),df.format(endDate)));
            return;
        }
        level++;
        if(startDate.compareTo(endDate) < 1) {
            long offset = startDate.getTime();
            long diff = endDate.getTime() - offset;
            long interval = diff/intervalCount;
            for (int i = 0 ; i <intervalCount;i++){
                Date start = new Date(offset);
                Date end ;
                if(i == (intervalCount-1)){
                    end = endDate;
                }else{
                    offset += interval;
                    end =  new Date(offset);
                }
                try{
                    logger.info("{}处理开始,层级:{},时间:{}", jobName,level,MessageFormat.format("开始时间:{0},结束时间:{1}",df.format(start),df.format(end)));
                    handler.deal(start,end);
                    logger.info("{}处理结束,层级:{},时间:{}", jobName,level,MessageFormat.format("开始时间:{0},结束时间:{1}",df.format(start),df.format(end)));
                }catch (MySQLNonTransientConnectionException ex){
                    logger.info("{}超时,进行拆分处理,层级:{}",jobName,level);
                    splitTime(start,end,level,jobName,handler);
                } catch (Exception e) {
                    logger.error("{}异常啦:{}",jobName, ExceptionUtils.getFullStackTrace(e));
                }
            }
        }
    }

    public interface TimeSplitHandler{
        void deal(Date start,Date end) throws Exception;
    }

    public JobTimeoutHelper setIntervalCount(int intervalCount) {
        this.intervalCount = intervalCount;
        return this;
    }

    public JobTimeoutHelper setMaxCount(int maxCount){
        this.maxCount = maxCount;
        return this;
    }


    public static void main(String[] args) {
        final Date today = new Date();
        final Date tomorrow = DateUtils.addDays(today, +1);
        final Date yestoday = DateUtils.addDays(today, -1);
        JobTimeoutHelper.create().setIntervalCount(2).setMaxCount(3)
                .splitTime(yestoday, tomorrow,"JobTimeoutHelper", new TimeSplitHandler() {
            @Override
            public void deal(Date start, Date end) throws Exception {
                throw new MySQLNonTransientConnectionException();
            }
        });
    }
}

这是一个任务超时帮助类,原来是通过递归的方式,不断将时间拆小,这样在庞大的数据面前也可以淡定的run起来。只需要多花点时间而已。同时,也可以配合多线程处理,让程序加速奔跑起来。

温馨提示:要根据实际需求,记得要设置多少等份,和最大层级哦。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部