技术开发 频道

提升性能 Java程序优化的一些非常好的实践

  为了快速地实现预期效果,在最初的实现中优先考虑了功能的实现,而未考虑系统性能和代码可读性等。系统大致有以下的实现:

  • REST API获取信息、数据库操作可能抛出的异常信息都被记录到日志文件中,作为调试用;

  • 共有5次数据库连接操作,包括第一次清空数据库表,针对两个外部系统D各有两次数据库插入操作,这5个连接都是独立的,用完之后即释放;

  • 所有的数据库插入语句都是使用java.sql.Statement类生成的;

  • 所有的数据库插入语句,都是单条执行的,即生成一条执行一条;

  • 整个过程都是在单个线程中执行的,包括数据库表清空操作,数据库插入操作,释放数据库连接;

  数据库插入操作的JDBC代码散布在代码中。虽然这个版本的系统可以正常运行,达到了预期的效果,但是效率很低,从通过 REST API获取信息,到解析并提取有效信息,再到数据库插入操作,总共耗时100秒左右。而预期的时间应该在一分钟以内,这显然是不符合要求的。

  三、代码优化过程

  笔者开始分析整个过程有哪些耗时操作,以及如何提升效率,缩短程序执行的时间。通过REST API获取信息,因为是使用外部系统提供的API,所以无法在此处提升效率;取得信息之后解析出有效部分,因为是对特定格式的信息进行解析,所以也无效率提升的空间。所以,效率可以大幅度提升的空间在数据库操作部分以及程序控制部分。下面,分条叙述对耗时操作的改进方法。

  1. 针对日志记录的优化

  关闭日志记录,或者更改日志输出级别。因为从两台服务器的外部系统D上获取到的信息是相同的,所以数据库插入操作会抛出异常,异常信息类似于“Attempt to insert duplicate record”,这样的异常信息跟有效信息的条数相等,有上千条。这种情况是能预料到的,所以可以考虑关闭日志记录,或者不关闭日志记录而是更改日志输出 级别,只记录严重级别(severe level)的错误信息,并将此类操作的日志级别调整为警告级别(warning level),这样就不会记录以上异常信息了。本项目使用的是 Java 自带的日志记录类,以下配置文件将日志输出级别设置为严重级别。

default file output is in user ’ s home directory.
levels can be: SEVERE, WARNING, INFO, FINE, FINER, FINEST
java.util.logging.ConsoleHandler.level
=SEVERE
java.util.logging.FileHandler.formatter
=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append
=true

  通过上述的优化之后,性能有了大幅度的提升,从原来的 100 秒左右降到了 50 秒左右。为什么仅仅不记录日志就能有如此大幅度的性能提升呢?查阅资料,发现已经有人做了相关的研究与实验。经常听到 Java 程序比 C/C++ 程序慢的言论,但是运行速度慢的真正原因是什么,估计很多人并不清楚。对于 CPU 密集型的程序(即程序中包含大量计算),Java 程序可以达到 C/C++ 程序同等级别的速度,但是对于 I/O 密集型的程序(即程序中包含大量 I/O 操作),Java 程序的速度就远远慢于 C/C++ 程序了,很大程度上是因为 C/C++ 程序能直接访问底层的存储设备。因此, 不记录日志而得到大幅度性能提升的原因是,Java 程序的 I/O 操作较慢,是一个很耗时的操作。

  2. 针对数据库连接的优化

  共享数据库连接。共有 5 次数据库连接操作,每次都需重新建立数据库连接,数据库插入操作完成之后又立即释放了,数据库连接没有被复用。为了做到共享数据库连接,可以通过单例模式 (Singleton Pattern)获得一个相同的数据库连接,每次数据库连接操作都共享这个数据库连接。这里没有使用数据库连接池(Database Connection Pool)是因为在程序只有少量的数据库连接操作,只有在大量并发数据库连接的时候才需要连接池。

public class JdbcUtil {
private static Connection con;
// 从配置文件读取连接数据库的信息
private static String driverClassName;
private static String url;
private static String username;
private static String password;
private static String currentSchema;
private static Properties properties = new Properties();
static {
// driverClassName, url, username, password, currentSchema 等从配置文件读取,代码略去
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
properties.setProperty(
"user", username);
properties.setProperty(
"password", password);
properties.setProperty(
"currentSchema", currentSchema);
try {
con
= DriverManager.getConnection(url, properties);
} catch (SQLException e) {
e.printStackTrace();
}
}
private JdbcUtil() {}
// 获得一个单例的、共享的数据库连接
public static Connection getConnection() {
return con;
}
public static void close() throws SQLException {
if (con != null)
con.close();
}
}
0
相关文章