记一次导数据经历
该博客没有什么技术含量,是一个技术废物的窘态记录!
这几天遇到了个无语的事情,某个客户的系统由于从java7升到了java8,导致系统崩了。
我就负责把数据库里的数据导出来,记录一下自己的窘态
毕竟写python写了好长时间,java又用的不好,窘态频出。
并且最妖的是,这个开发只能远程,不能测试,也就是连不到数据库。
先说需求吧,很简单:在配置文件中配置到导出的时间段,然后运行程序。
最开始,设置成了按整个时间段查询,直接卡在了查询上。
和客户了解了一下,一天的数据量大概是1亿
下面的这个程序就是 输入 起始日期和结束日期就会返回一个列表,用于之后的sql语句拼接
public static List<ArrayList<String>> getDateList(String startDate, String endDate) throws ParseException {
String begainDate = formatDateSting(StringDateClever(startDate));
String finishDate = formatDateSting(StringDateClever(endDate));
List<ArrayList<String>> dateList = new ArrayList<>();
while (begainDate.compareTo(finishDate) <= 0) {
List<String> subList = new ArrayList<>();
subList.add(begainDate);
String nextDate = nextHalfHour(begainDate);
subList.add(nextDate);
dateList.add(new ArrayList<>(subList));
begainDate = nextDate;
}
dateList.remove(dateList.size() - 1);
return dateList;
}
public static String nextHalfHour(String str) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str);
long time = date.getTime() + 1000L * 60 * 10;
date = new Date(time);
return sdf.format(date);
}
public static Date StringDateClever(String str) {
Date date = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (str.length() == 19) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
} else if (str.length() == 16) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
} else if (str.length() == 13) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH");
} else if (str.length() == 10) {
sdf = new SimpleDateFormat("yyyy-MM-dd");
}
date = sdf.parse(str);
} catch (ParseException e) {
// System.out.println(e.getMessage());
}
return date;
}
public static String formatDateSting(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strDate = sdf.format(date);
return strDate;
}
然后 交互式对客户不友好,改成读配置文件,程序不能运行一次,要加上重试。
下面就是 怎么读取配置文件(config/init.properties)
FileInputStream fileInputStream = new FileInputStream("config/init.properties");
pro.load(fileInputStream);// 相对路径
startDate = pro.getProperty("startDate");
endDate = pro.getProperty("endDate");
FINISHDATE = pro.getProperty("endDate");
row = Integer.parseInt(pro.getProperty("row"));
retryCount = Integer.parseInt(pro.getProperty("retryCount"));
LOG.info("初始化配置成功");
这样就将 起始日期 和结束日期 和重试次数 都初始化了。
然后 就是断点查询,不能断了在启动还要重新跑吧,数据量那么大,这谁顶得住呢。
那就在查询出错或者有其他异常的时候记录断点
记录完断点就得在整个程序初始化的时候检查是否存在断点
记录断点的时候 我是这样处理的,记录下当前查询的时间段
startDate就是 新的起始时间
那么endDate就不是结束时间,只是startDate +10分钟的时间,所以还需要个FinishDate在读配置文件的时候记录下endDate
public static void writeCheckPoint(String stratDate, String endDate, String finishDate, int row) throws Exception {
String path = System.getProperty("user.dir") + "./log/checkPoint.txt";
File file = new File(path);
if (!file.exists()) {
file.getParentFile().mkdir();
try {
file.createNewFile();
} catch (IOException e) {
System.out.println("创建文件时失败");
e.printStackTrace();
}
}
String string = stratDate + "|" + endDate + "|" + finishDate + "|" + String.valueOf(row);
FileWriter fw = null;
try {
fw = new FileWriter(file, false);
fw.write(string);
} catch (IOException e) {
System.out.println("断点文件输出失败");
e.printStackTrace();
} finally {
try {
if (fw != null)
fw.close();
} catch (IOException e) {
System.out.println("断点文件关闭失败");
e.printStackTrace();
}
}
checkPointFlag = true;
flag = false;
throw new Exception("查询异常");
}
断点记录有了,那初始化时读断点的操作也得有
这里,我设置了一个标记位(checkPointFlag)来记录当前是否存在断点
public static void checkPointExist() {
String path = System.getProperty("user.dir") + "./log/checkPoint.txt";
File file = new File(path);
if (file.exists()) {
checkPointFlag = true;
}
}
下一步 该 记录数据,读写数据了。
记录数据就用List<hashmap>就好了
写数据的时候要注意使用StringBuilder
奈何自己渣,这都不知道。
String是不可变类型,所以在进行 “你好“ + “世界” 这样的拼接操作的时候。
是创建了新的内存地址,并将指针指向 “你好世界”。
这样对资源一种浪费。
内存中的无用的对象多了之后,GC处理的工作也就变多了,很影响效率。
(尤其是数据量很大并且有很多拼接的操作时)、
并且如果New String速度快于GC的清理的话,可能会导致内存溢出,一定要注意!
如果是多线程的话就用StringBuffer,这个类型线程安全。
上一篇: 一次搞怪的经历
下一篇: 记一次读取数据的经历