昨天在项目中需要对日志的查询结果进行导出功能。
日志导出功能的实现是这样的,输入查询条件,然后对查询结果进行导出。由于日志数据量比较大。多的时候,有上亿条记录。
之前的解决方案都是多次查询,然后使用limit 限制每次查询的条数。然后导出。这样的结果是效率比较低效。
那么能不能一次查询就把所有结果倒出来了?于是我就使用一次查询,不使用limit分页。结果出现 java.lang.OutOfMemoryError: Java heap space问题。
看来是DB服务器端将一次将查询到的结果集全部发送到Java端保存在内存中。由于结果集比较大,所以出现OOM问题。
首先我想到的是游标功能。那么是不是可以使用游标,一次从服务器端慢慢的取呢?上网查询了一下,大家都说MySQL不支持游标功能等等。
后来就去看JDBC代码。找到了setFetchSize()方法,结果设置以后,却不能生效,还是出现OOM问题。
我的设置如下
[java] view plaincopy
ps=conn.con.prepareStatement("select * from bigTable");
ps.setFetchSize(1000);
后来老大在MySQL看到了这样的方法:
[java] view plaincopy
ps = (PreparedStatement) con.prepareStatement("select * from bigTable",
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(Integer.MIN_VALUE);
ps.setFetchDirection(ResultSet.FETCH_REVERSE);
对此解释是:MySQL JDBC默认客户端数据接收方式为如下:
默认为从服务器一次取出所有数据放在客户端内存中,fetch size参数不起作用,当一条SQL返回数据量较大时可能会出现JVM OOM。
要一条SQL从服务器读取大量数据,不发生JVM OOM,可以采用以下方法之一:
1、当statement设置以下属性时,采用的是流数据接收方式,每次只从服务器接收部份数据,直到所有数据处理完毕,不会发生JVM OOM。
setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
setFetchSize(Integer.MIN_VALUE);
2、调用statement的enableStreamingResults方法,实际上enableStreamingResults方法内部封装的就是第1种方式。
3、设置连接属性useCursorFetch=true (5.0版驱动开始支持),statement以TYPE_FORWARD_ONLY打开,再设置fetch size参数,表示采用服务器端游标,每次从服务器取fetch_size条数据。
设置以后,果然可以解决我的问题。
附上代码:
[java] view plaincopy
package com.seven.dbTools.DBTools;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
public class JdbcHandleMySQLBigResultSet {
public static long importData(String sql){
String url = "jdbc:mysql://ipaddress:3306/test?user=username&password=password";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
long allStart = System.currentTimeMillis();
long count =0;
Connection con = null;
PreparedStatement ps = null;
Statement st = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection(url);
ps = (PreparedStatement) con.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(Integer.MIN_VALUE);
ps.setFetchDirection(ResultSet.FETCH_REVERSE);
rs = ps.executeQuery();
while (rs.next()) {
//此处处理业务逻辑
count++;
if(count%600000==0){
System.out.println(" 写入到第 "+(count/600000)+" 个文件中!");
long end = System.currentTimeMillis();
}
}
System.out.println("取回数据量为 "+count+" 行!");
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null){
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(con!=null){
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return count;
}
public static void main(String[] args) throws InterruptedException {
String sql = "select * from test.bigTable ";
importData(sql);
}
}
最近对JDBC有了进一步的了解。关于JDBC,推荐我的另一篇文章,用于解决不写文件,从Java IO流中直接导入数据到MySQL:
分享到:
相关推荐
具体而言,当使用`setFirstResult()`和`setMaxResults()`方法对查询结果集进行限制时,如果结果集被标记为只进(forward-only),则不支持这些操作,从而引发异常。这是因为只进结果集是为了提高性能而设计的,它...
6. **处理结果**:如果你执行的是查询语句,可以使用`ResultSet`对象来遍历和处理返回的结果集。 7. **关闭资源**:在完成所有数据库操作后,记得关闭连接、Statement和ResultSet,以释放系统资源: ```java conn...
- 使用Statement的`setFetchSize()`方法控制结果集的大小,降低内存消耗。 ### 7. JDBC与数据库的元数据 通过Connection对象的`getMetaData()`方法可以获得关于数据库的信息,如表结构、列信息等,这对于数据库...
在处理大数据查询时,经常遇到的一个问题是由于查询结果集过大而导致的性能瓶颈,尤其是当这些数据需要被加载到客户端应用程序的内存中时。这种情况下的常见错误是`Java.lang.OutOfMemoryError:Java heap space`,即...
8. **性能优化**:`sqljdbc.jar` 支持一些高级特性,如`Statement.setFetchSize()` 和 `ResultSet.setFetchSize()` 可以控制结果集的缓存大小,减少内存消耗;`Statement.setMaxRows()` 可以限制查询返回的最大行数...
在Java中读取Access数据库的数据通常涉及使用Java Database Connectivity (JDBC) API。本文将详细介绍如何使用JDBC连接到Access数据库并进行分页读取数据,同时也会提及相关的库和示例代码。 首先,我们需要一个能...
在Java开发中,处理大数据量是常见的需求之一,特别是当数据规模达到几百万级别时,如何高效、稳定地处理这些数据变得尤为重要。本文将详细介绍一种Java一次性查询处理几百万数据的有效方法,并通过具体代码示例进行...
这个驱动程序实现了JDBC API,使得Java开发者能够利用Java语言来执行SQL语句、管理数据库事务以及处理结果集。以下是关于SQLJDBC_4.0驱动的一些关键知识点: 1. **JDBC接口**:JDBC是Java Database Connectivity的...
5. **处理结果集**:对于查询操作,使用 `ResultSet` 处理结果集。 6. **关闭资源**:在操作完成后,关闭 `Statement` 和 `Connection` 以释放资源。 #### 五、各种 execute 方法 JDBC 提供了多种执行 SQL 语句的...
在IT行业中,数据滚动通常指的是在查询大量数据时,能够按需加载和浏览数据,而不是一次性加载所有数据,这在处理大数据集时非常有用,以提高性能和用户体验。 描述中的 "通过JDBC连接数据库" 提到的是Java ...
总的来说,sqljdbc4-2.0.jar是连接Java应用程序和SQL Server数据库的关键组件,正确选择和使用它可以确保你的Java应用能够顺利地与数据库进行交互。在实际项目中,务必根据你的JRE版本和需求选择合适的驱动版本。
SQL Server 2005 JDBC驱动是微软公司为Java开发者提供的一个关键组件,它使得Java应用程序能够连接到SQL Server数据库,执行SQL查询,以及管理数据。JDBC(Java Database Connectivity)是Java平台的标准接口,用于...
- 使用`Statement`的`setFetchSize()`调整结果集的缓冲大小,减少网络传输。 - 使用连接池(如C3P0、HikariCP)管理数据库连接,提高性能和资源利用率。 - 调整SQL语句,避免全表扫描,使用索引,减少JOIN操作等...
3. **结果集缓存**:通过ResultSet的`setFetchSize()`方法,可以预先获取一定数量的数据,提高性能。 4. **游标支持**:允许前后移动结果集,方便处理大量数据。 5. **元数据查询**:获取表结构、列信息等数据库元...
5. **处理结果集**: 如果是查询操作,遍历ResultSet对象,获取数据。 6. **关闭资源**: 最后,关闭ResultSet、Statement和Connection对象,释放数据库资源。 ### 数据库连接池 在实际应用中,为了提高性能和资源...
总结来说,JDBC2是Java与数据库交互的一个强大工具,它通过批处理、PreparedStatement、滚动结果集、存储过程、连接池、分布式事务和详细的异常处理等特性,极大地提升了开发效率和应用性能。对于任何Java数据库应用...
综上所述,JDBC中的`absolute()`方法提供了一种分页策略,适用于需要直接定位结果集特定位置的场景。不过,对于大多数分页需求,更常见的是使用数据库提供的分页机制,如`LIMIT`和`OFFSET`,或者使用存储过程。在...
为了提高性能,可以使用`Statement.setFetchSize()`方法设定每次从数据库获取的数据量,以控制网络传输的开销。 总的来说,JDBC分页技术是数据库操作中不可或缺的一部分,理解和掌握不同数据库的分页方式对于优化...
Snowflake JDBC驱动是Java应用程序与Snowflake数据仓库之间的桥梁,它实现了Java的JDBC API,使得开发人员可以使用标准的Java SQL接口来连接、查询和操作Snowflake数据库。该驱动支持多种功能,包括连接建立、执行...
Java的PreparedStatement接口允许我们设置游标类型和获取结果集的方式,例如设置为ResultSet.TYPE_FORWARD_ONLY和ResultSet.CONCUR_READ_ONLY,这可以防止数据被一次性加载到内存。同时,通过调用setFetchSize方法...