背景
这边我们做了一个产品的小功能,把mysql的数据拉取到hdfs上来
代码实现
代码很简单,就是简单的JDBC
long start = System.currentTimeMillis();
PreparedStatement preparedStatement = connection.prepareStatement("select * from ceshi");
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
long end = System.currentTimeMillis();
System.out.println("------"+Thread.currentThread()+"获取数据消耗时间"+(end-start)/1000);
读取数据,然后hdfs的IO流写入到hdfs上面。但是我发现在读取数据的时候会出现卡顿的情况。(我这边数据量大约100W),我打印了日志,发现在接收数据的时候,耗时大约在10秒,
后来开始查资料发现mysql查询的机制是jdbc默认的读取数据的时候,会将要查询的数据一次性读到内存中,再通过resultSet循环读取出来。
调研了下发现可以通过JDBC 流的方式来读取数据,这种方式可以将读取出来的每一条数据查询到客户端,再执行insert操作,这样机器负载就会轻很多。
相关源码如下
/**
* We only stream result sets when they are forward-only, read-only, and the
* fetch size has been set to Integer.MIN_VALUE
*
* @return true if this result set should be streamed row at-a-time, rather
* than read all at once.
*/
protected boolean createStreamingResultSet() {
return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
&& (this.query.getResultFetchSize() == Integer.MIN_VALUE));
}
然后之前的代码如下
long start = System.currentTimeMillis();
PreparedStatement preparedStatement = connection.prepareStatement("select * from ceshi",ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
preparedStatement.setFetchSize(Integer.MIN_VALUE);
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
long end = System.currentTimeMillis();
System.out.println("------"+Thread.currentThread()+"获取数据消耗时间"+(end-start)/1000);
然后就会发现获取数据不会消耗时间,大大提升了效率。
顺便说一下 不添加 这两个参数ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY 也可以,因为mysql connection.prepareStatement(sql)中就默认设置这两个参数的。感兴趣的可以直接看下源码。
所以为了方便也可以直接设置
preparedStatement.setFetchSize(Integer.MIN_VALUE);
参考资料:https://blog.csdn.net/zhouxukun123/article/details/83476715
网友评论