随着项目上线,业务数据会越来越多。这个时候,很多开发时适用的方法、任务,在庞大数据量面前就会变得很不堪,经常会出现超时,慢查询,异常等等问题。
所以,一般在开发阶段,我们怎么能避免这些问题呢?一切皆有套路。
一般处理套路:
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起来。只需要多花点时间而已。同时,也可以配合多线程处理,让程序加速奔跑起来。
温馨提示:要根据实际需求,记得要设置多少等份,和最大层级哦。