1

I'm trying to create a batch program. The problem is that table could have two million of rows and this could produce a problem of memory. In my job I have DB2 and in my home to test I used mysql.

I've debugged with eclipse, and I viewed that resultset cointains all rows.

I've searched in web page but I haven't found the solution. For example, my test code is:

    package misCodigos;

    import java.io.FileWriter;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;

    public class testLimitDB {

        // JDBC driver name and database URL
        static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
        static final String DB_URL = "jdbc:mysql://localhost/testing";

        //  Database credentials
        static final String USER = "root";
        static final String PASS = "8182";


        private static FileWriter fw=null;

        public static void main(String[] args) {
            // Clase que pretende mostrar cómo insertar filas en java
            Connection conn = null;
            Statement stmt = null;


            try{

                //STEP 2: Register JDBC driver
                Class.forName("com.mysql.jdbc.Driver");

                //STEP 3: Open a connection
                System.out.println("Connecting to database...");
                conn = DriverManager.getConnection(DB_URL,USER,PASS);

                //STEP 4: Execute a query
                //  System.out.println("Creating statement...");
                stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
                        java.sql.ResultSet.CONCUR_READ_ONLY);
                String sql = "select * from tablaTest";


                PreparedStatement pstmt;
                pstmt = conn.prepareStatement(sql);

                conn.setAutoCommit(false);
                pstmt.setFetchSize(200);

                ResultSet rs = pstmt.executeQuery(sql);
                System.out.println("Starting to retrieve data. Memory Used: " + getUsedMemorySize());

                //System.out.println(rs.getString(rs.last()));

                /*          rs.last(); 
                int numRows = rs.getRow(); 
                System.out.println("Numero de filas: " + numRows);*/

                fw = new FileWriter("test.out");
                while (rs.next()) {
                    fw.write(rs.getInt(1) + ";" + rs.getString(2) + "\n");
                }
                System.out.println("Done retrieving data => Memory Used: "  + getUsedMemorySize());

                pstmt.close();
                conn.close();
                fw.flush();
                fw.close();

            }catch(Exception e){
                //Handle errors for Class.forName
                e.printStackTrace();
            }finally{
                //finally block used to close resources

                try{

                    if (fw != null){
                        fw.flush();
                        fw.close();      
                    }

                    if(stmt!=null) {
                        stmt.close();
                    }
                }catch(SQLException | IOException se2){
                }// nothing we can do
                try{
                    if(conn!=null) {
                        conn.close();
                    }
                }catch(SQLException se){
                    se.printStackTrace();
                }//end finally try
            }//end try
            System.out.println("Finalizado!");


        }

        public static long getUsedMemorySize() {

            long freeSize = 0L;
            long totalSize = 0L;
            long usedSize = -1L;
            try {
                Runtime info = Runtime.getRuntime();
                freeSize = info.freeMemory();
                totalSize = info.totalMemory();
                usedSize = totalSize - freeSize;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return usedSize;

        }

    }

Could anyone help me?

Thanks

PD: I created the code to db2. I'm not sure if the resultset fetch only the rows that I like because I lanched with debug mode in eclipse and the resulset variable, now it is very different and it appears encrypt.

import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class testLimitDB {

    // JDBC driver name and database URL
    //static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  

    static final String JDBC_DRIVER = "com.ibm.db2.jcc.DB2Driver";  
    //static final String DB_URL = "jdbc:mysql://localhost/testing";
    static final String DB_URL = "jdbc:db2://localhost:50000/SAMPLE";

    //  Database credentials MYSQL
    //static final String USER = "root";

    static final String USER = "db2admin";
    static final String PASS = "8182";


    private static FileWriter fw=null;

    public static void main(String[] args) {
        // Clase que pretende mostrar cómo insertar filas en java

        Connection conn = null;
        Statement stmt = null;


        try{

            //STEP 2: Register JDBC driver
            Class.forName(JDBC_DRIVER);

            //STEP 3: Open a connection
            System.out.println("Connecting to database...");
            conn = DriverManager.getConnection(DB_URL,USER,PASS);

            //STEP 4: Execute a query
            //  System.out.println("Creating statement...");
            stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
                    java.sql.ResultSet.CONCUR_READ_ONLY);
            String sql = "select * from FELIPE.TABLATEST";


            PreparedStatement pstmt;
            pstmt = conn.prepareStatement(sql);

            conn.setAutoCommit(false);
            pstmt.setFetchSize(999);

            ResultSet rs = pstmt.executeQuery();
            System.out.println("Starting to retrieve data. Memory Used: " + getUsedMemorySize());

            //System.out.println(rs.getString(rs.last()));

            /*          rs.last(); 
            int numRows = rs.getRow(); 
            System.out.println("Numero de filas: " + numRows);*/

            fw = new FileWriter("test.out");
            while (rs.next()) {
                fw.write(rs.getInt(1) + ";" + rs.getString(2) + "\n");
            }
            System.out.println("Done retrieving data => Memory Used: "  + getUsedMemorySize());

            pstmt.close();
            conn.close();
            fw.flush();
            fw.close();

        }catch(Exception e){
            //Handle errors for Class.forName
            e.printStackTrace();
        }finally{
            //finally block used to close resources

            try{

                if (fw != null){
                    fw.flush();
                    fw.close();      
                }

                if(stmt!=null) {
                    stmt.close();
                }
            }catch(SQLException | IOException se2){
            }// nothing we can do
            try{
                if(conn!=null) {
                    conn.close();
                }
            }catch(SQLException se){
                se.printStackTrace();
            }//end finally try
        }//end try
        System.out.println("Finalizado!");


    }

    public static long getUsedMemorySize() {

        long freeSize = 0L;
        long totalSize = 0L;
        long usedSize = -1L;
        try {
            Runtime info = Runtime.getRuntime();
            freeSize = info.freeMemory();
            totalSize = info.totalMemory();
            usedSize = totalSize - freeSize;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return usedSize;

    }

}

I tried again and my conclusions are the drivers (mysql and db2) work correctly. I tested with jvisualvm and I viewed clear that the memory changed with the size of cursor. I'll write a document with differents conclusions and I'll post it.

felipin_85
  • 13
  • 4

1 Answers1

1

I think that your problem is coming from testing with MySQL that does not has server side cursors and selecting large result set inevitably leads to OOM no matter if you set FETCH size or NOT set it.

DB2 does not have the same problem due to the cursors and the driver will not throw OOM. You need to define a well suited Fetch Size. If you have a large bulk job I would use a large value. In other words, when you do testing test against the same environment :)

Alexander Petrov
  • 9,723
  • 24
  • 57
  • Thanks Alexandre! I'll try testing in DB2, and I will let you know. :) – felipin_85 Mar 27 '16 at 17:17
  • Alexandre, I'm not sure if the behaivour is correct, because the resultset is very differente that mysql and all fields are letter without meaning. – felipin_85 Mar 27 '16 at 19:55
  • Well I guess if it throws OOM with Mysql and it does not throw OOM with DB2 it should at least fix the OOM exception. On the other hand if you want to be 100% sure on how it is acting you can use a program for SQL monitoring for DB2 like for example Query Monitor. There you can observe how your SQL is executed. Alternativly you may sniff the network traffic in order to see how many remote calls are done, this is if you want to verify the Fetch Size. B – Alexander Petrov Mar 27 '16 at 21:05
  • Hi Alexandre, I tried again and my conclusions are the drivers (mysql and db2) work correctly. I tested with jvisualvm and I viewed clear that the memory changed with the size of cursor. – felipin_85 Apr 03 '16 at 08:46