记一次部署项目踩过的坑
7月初公司接到一个项目,很简单ssm的项目,其中有一块就是数据入库。
编写入库代码,我擅长的很啊,对不对,和磊哥怎么也算是混了一年,不会个入库,岂不是很丢人。
刷刷刷……
没错。很快这个脚本就如火如荼的用上了。
结果真是不尽如人意,是的,没错,报错了!!!
java.lang.OutOfMemoryError: Java heap space 堆内存异常,不得不说,bug真是程序员的好朋友,通过这一周的学习,我学习了堆内存溢出的原因,解决方案,如何调整jar包的内存大小等等等等。
且慢,尽管学习了这么多,但是问题还是没解决,这尼玛。原始代码如下:
/**
* 数值预报
*
* @author gaoyongqin
*
*/
public class SzybDB {
public static void intoDB(File inFile) {
// 经度,纬度,温度,过去一小时降水
// Lat,Lon,TEM,PRE_1h
PreparedStatement prep = null;
Connection con = C3P0Inner.getConnection();
String foreignKeySql = "insert into data_szyb values";
StringBuilder foreignKeyValue = new StringBuilder();
int i = 0;
String str = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
BufferedReader br = null;
String format = sdf.format(new Date());
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(inFile)));
while ((str = br.readLine()) != null) {
i++;
// 该文件第一行直接就是数据,所以不用过滤掉表头
foreignKeyValue.append(",").append("(").append("null").append(",'");
String replace = str.replace(" ", "','");
String name = inFile.getName();
foreignKeyValue.append(replace).append("','").append(name.substring(0, name.indexOf(".")));
foreignKeyValue.append("','").append(format).append("')");
if (i % 5000 == 0) {
try {
foreignKeyValue.deleteCharAt(0);
prep = con.prepareStatement(foreignKeySql + foreignKeyValue.toString()
+ "ON DUPLICATE KEY UPDATE TEM=VALUES(TEM),PRE_1h=VALUES(PRE_1h)"
+ ",updateTime=VALUES(updateTime)");
prep.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
foreignKeyValue = new StringBuilder();
}
}
if (foreignKeyValue != null && !"".equals(foreignKeyValue)) {
try {
foreignKeyValue.deleteCharAt(0);
prep = con.prepareStatement(foreignKeySql + foreignKeyValue.toString()
+ "ON DUPLICATE KEY UPDATE TEM=VALUES(TEM),PRE_1h=VALUES(PRE_1h)"
+ ",updateTime=VALUES(updateTime)");
prep.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("数值预报数据在 :" + sdf.format(new Date()) + " 时,入库成功");
} else {
System.out.println("数值预报数据在 :" + sdf.format(new Date()) + " 时,无数据");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
if (prep != null) {
prep.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
丝毫不夸张,我找了我代码的原因找了一周,甚至于我一度认为我的代码没有任何的问题,肯定是jar包进程的内存开的不够大,我还请教了磊哥。迪哥,喜喜。磊哥还教我如何使用jmap等工具
nohup java -Djava.library.path=/home/qht/jdk1.7.0_79/jre/bin -Xms4096m -Xmx4096m -XX:+UseG1GC -jar pythonListen.jar zdyb &>log.log &
/home/qht/jdk1.7.0_79/bin/jmap -heap 30869
/home/qht/jdk1.7.0_79/bin/jmap -histo:live 30869
我调整了运行jar包的进程内存大小,自己得意的不行,安全的跑了一天的数据,第二天悲催的又堆内存溢出了,这下我不淡定了。感情没几天就要去内蒙部署项目了,现在还在这崩着呢!其中除了内存溢出,还有一个错误:
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 59,977 milliseconds ago. The last packet sent successfully to the server was 1 milliseconds ago.
链接超时了,我压根儿没把这两个错误联系在一起,这为我后面找了这个bug找了两周埋下了伏笔。讲真的,我又学会了如何去处理这个链接超时:
参考:https://www.cnblogs.com/chrischennx/p/7279215.html?utm_source=itdadao&utm_medium=referral
第一种:调节wait_timeout,
设置mysql超时:
mysql -uroot -p -h 127.0.0.1
set global interactive_timeout=31536000;
set global wait_timeout=31536000;
将wait_timeout设置成一年。
第二种:
为了避免空闲时间过长超过最大空闲时间而被断开,我们设置三个配置:
validationQuery: SELECT 1
testWhileIdle: true
timeBetweenEvictionRunsMillis: 28000
其中timeBetweenEvictionRunsMillis
需要小于mysql的wait_timeout
。
但是这种方法无法避免重启的情况,不过一般数据库不会频繁重启,影响不大,如果非得频繁重启,可以通过设置testOnBorrow
,即申请连接的时候先试一试连接是否可用,不过带来的影响就是性能降低,需要根据实际需求合理取舍。
其中因为我是用的c3p0链接的数据库,所以设置的参数有点不一样,但是想法是一样的。
参考:https://blog.csdn.net/mmake1994/article/details/86561940
好了,后面再说一下那个内存溢出吧!!!
后面其实我想通了,为什么内存溢出了,然后就是链接超时了,哦~~~~(你应该了解我的心情。)
那大约就应该是链接没有关闭???抱着这样的想法,我改了我的代码!
/**
* 数值预报
*
* @author gaoyongqin
*
*/
public class SzybDB {
public static void intoDB(File inFile) {
// 经度,纬度,温度,过去一小时降水
// Lat,Lon,TEM,PRE_1h
PreparedStatement prep = null;
Connection con = C3P0Inner.getConnection();
String foreignKeySql = "insert into data_szyb values";
StringBuilder foreignKeyValue = new StringBuilder();
int i = 0;
String str = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
BufferedReader br = null;
String format = sdf.format(new Date());
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(inFile)));
while ((str = br.readLine()) != null) {
i++;
// 该文件第一行直接就是数据,所以不用过滤掉表头
foreignKeyValue.append(",").append("(").append("null").append(",'");
String replace = str.replace(" ", "','");
String name = inFile.getName();
foreignKeyValue.append(replace).append("','").append(name.substring(0, name.indexOf(".")));
foreignKeyValue.append("','").append(format).append("')");
if (i % 5000 == 0) {
try {
foreignKeyValue.deleteCharAt(0);
prep = con.prepareStatement(foreignKeySql + foreignKeyValue.toString()
+ "ON DUPLICATE KEY UPDATE TEM=VALUES(TEM),PRE_1h=VALUES(PRE_1h)"
+ ",updateTime=VALUES(updateTime)");
prep.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (prep != null) {
prep.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
foreignKeyValue = new StringBuilder();
}
}
if (foreignKeyValue != null && !"".equals(foreignKeyValue)) {
try {
foreignKeyValue.deleteCharAt(0);
prep = con.prepareStatement(foreignKeySql + foreignKeyValue.toString()
+ "ON DUPLICATE KEY UPDATE TEM=VALUES(TEM),PRE_1h=VALUES(PRE_1h)"
+ ",updateTime=VALUES(updateTime)");
prep.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("数值预报数据在 :" + sdf.format(new Date()) + " 时,入库成功");
} else {
System.out.println("数值预报数据在 :" + sdf.format(new Date()) + " 时,无数据");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
if (prep != null) {
prep.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
仔细对比代码,没错,我在每一次链接完数据库之后都会在finally里把prep链接关闭。然后结合着jmap查看,果然,正如我所料。一切都是那么的好,那么的优秀,哎呀,困扰我3周的问题就这样解决了,我真是心情大好,也不知道怎么了,自己也工作了这么久,还是这么的……菜!!我真是有点悲哀啊!
废话不多说,也算是学习到了一点点的东西吧,让我在这条路上是铺砖添瓦了!
打完,收工!
上一篇: 记一次完整的项目部署
下一篇: 记录一次老Android项目迁移过程