`
一笑_奈何
  • 浏览: 69226 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

正确使用MySQL JDBC setFetchSize()方法解决JDBC处理大结果集 java.lang.OutOfMemoryError: Java hea

    博客分类:
  • J2SE
阅读更多
昨天在项目中需要对日志的查询结果进行导出功能。

日志导出功能的实现是这样的,输入查询条件,然后对查询结果进行导出。由于日志数据量比较大。多的时候,有上亿条记录。

之前的解决方案都是多次查询,然后使用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:
分享到:
评论

相关推荐

    com.microsoft.sqlserver.jdbc.SQLServerException: 只进结果集不支持请求的操作 解决方案

    具体而言,当使用`setFirstResult()`和`setMaxResults()`方法对查询结果集进行限制时,如果结果集被标记为只进(forward-only),则不支持这些操作,从而引发异常。这是因为只进结果集是为了提高性能而设计的,它...

    My sql 驱动程序

    6. **处理结果**:如果你执行的是查询语句,可以使用`ResultSet`对象来遍历和处理返回的结果集。 7. **关闭资源**:在完成所有数据库操作后,记得关闭连接、Statement和ResultSet,以释放系统资源: ```java conn...

    JDBC教程

    - 使用Statement的`setFetchSize()`方法控制结果集的大小,降低内存消耗。 ### 7. JDBC与数据库的元数据 通过Connection对象的`getMetaData()`方法可以获得关于数据库的信息,如表结构、列信息等,这对于数据库...

    利用JDBC解决大数据查询的问题

    在处理大数据查询时,经常遇到的一个问题是由于查询结果集过大而导致的性能瓶颈,尤其是当这些数据需要被加载到客户端应用程序的内存中时。这种情况下的常见错误是`Java.lang.OutOfMemoryError:Java heap space`,即...

    sqljdbc.jar包

    8. **性能优化**:`sqljdbc.jar` 支持一些高级特性,如`Statement.setFetchSize()` 和 `ResultSet.setFetchSize()` 可以控制结果集的缓存大小,减少内存消耗;`Statement.setMaxRows()` 可以限制查询返回的最大行数...

    java读取*.mdb文件数据

    在Java中读取Access数据库的数据通常涉及使用Java Database Connectivity (JDBC) API。本文将详细介绍如何使用JDBC连接到Access数据库并进行分页读取数据,同时也会提及相关的库和示例代码。 首先,我们需要一个能...

    java一次性查询处理几百万数据解决方法

    在Java开发中,处理大数据量是常见的需求之一,特别是当数据规模达到几百万级别时,如何高效、稳定地处理这些数据变得尤为重要。本文将详细介绍一种Java一次性查询处理几百万数据的有效方法,并通过具体代码示例进行...

    sqljdbc_4.0

    这个驱动程序实现了JDBC API,使得Java开发者能够利用Java语言来执行SQL语句、管理数据库事务以及处理结果集。以下是关于SQLJDBC_4.0驱动的一些关键知识点: 1. **JDBC接口**:JDBC是Java Database Connectivity的...

    数据库连接JDBC详解

    5. **处理结果集**:对于查询操作,使用 `ResultSet` 处理结果集。 6. **关闭资源**:在操作完成后,关闭 `Statement` 和 `Connection` 以释放资源。 #### 五、各种 execute 方法 JDBC 提供了多种执行 SQL 语句的...

    Db.rar_滚动

    在IT行业中,数据滚动通常指的是在查询大量数据时,能够按需加载和浏览数据,而不是一次性加载所有数据,这在处理大数据集时非常有用,以提高性能和用户体验。 描述中的 "通过JDBC连接数据库" 提到的是Java ...

    sqljdbc4-2.0.jar

    总的来说,sqljdbc4-2.0.jar是连接Java应用程序和SQL Server数据库的关键组件,正确选择和使用它可以确保你的Java应用能够顺利地与数据库进行交互。在实际项目中,务必根据你的JRE版本和需求选择合适的驱动版本。

    sql server 2005 jdbc 驱动

    SQL Server 2005 JDBC驱动是微软公司为Java开发者提供的一个关键组件,它使得Java应用程序能够连接到SQL Server数据库,执行SQL查询,以及管理数据。JDBC(Java Database Connectivity)是Java平台的标准接口,用于...

    sqlserver2000 jdbc驱动

    - 使用`Statement`的`setFetchSize()`调整结果集的缓冲大小,减少网络传输。 - 使用连接池(如C3P0、HikariCP)管理数据库连接,提高性能和资源利用率。 - 调整SQL语句,避免全表扫描,使用索引,减少JOIN操作等...

    Sql 2005 企业版JDBC驱动程序

    3. **结果集缓存**:通过ResultSet的`setFetchSize()`方法,可以预先获取一定数量的数据,提高性能。 4. **游标支持**:允许前后移动结果集,方便处理大量数据。 5. **元数据查询**:获取表结构、列信息等数据库元...

    JDBC入门基础讲座

    5. **处理结果集**: 如果是查询操作,遍历ResultSet对象,获取数据。 6. **关闭资源**: 最后,关闭ResultSet、Statement和Connection对象,释放数据库资源。 ### 数据库连接池 在实际应用中,为了提高性能和资源...

    JDBC2

    总结来说,JDBC2是Java与数据库交互的一个强大工具,它通过批处理、PreparedStatement、滚动结果集、存储过程、连接池、分布式事务和详细的异常处理等特性,极大地提升了开发效率和应用性能。对于任何Java数据库应用...

    JDBC分页 absolute实现

    综上所述,JDBC中的`absolute()`方法提供了一种分页策略,适用于需要直接定位结果集特定位置的场景。不过,对于大多数分页需求,更常见的是使用数据库提供的分页机制,如`LIMIT`和`OFFSET`,或者使用存储过程。在...

    JDBC分页技术....................

    为了提高性能,可以使用`Statement.setFetchSize()`方法设定每次从数据库获取的数据量,以控制网络传输的开销。 总的来说,JDBC分页技术是数据库操作中不可或缺的一部分,理解和掌握不同数据库的分页方式对于优化...

    snow-jdbc:Snowflake JDBC驱动程序

    Snowflake JDBC驱动是Java应用程序与Snowflake数据仓库之间的桥梁,它实现了Java的JDBC API,使得开发人员可以使用标准的Java SQL接口来连接、查询和操作Snowflake数据库。该驱动支持多种功能,包括连接建立、执行...

    Java ResultSet导出大数据.docx

    Java的PreparedStatement接口允许我们设置游标类型和获取结果集的方式,例如设置为ResultSet.TYPE_FORWARD_ONLY和ResultSet.CONCUR_READ_ONLY,这可以防止数据被一次性加载到内存。同时,通过调用setFetchSize方法...

Global site tag (gtag.js) - Google Analytics