본문 바로가기

Dev.../플밍 관련 자료

[펌] 대용량 insert/update/delete || 배치작업

참조 : www.javapattern.info

 

Database에서 퍼포먼스 향상 및 불필요한 작업의 수를 줄이려한다면
매번 발생하는 Insert/Update/Delete처리를 한번의 배치작업으로 처리할 수 있다.
JDBC 2.0의 addBatch(), executeBatch()로 구현할 수 있다.

또한 Oracle에서는 Oracle-specific model이라는 것이 존재하며 위의 기능 제공해 준다.


* JDBC 2.0

장 : 범용성( JDBC2.0을 지원하는 모든 DB에서 사용가능)
단 : 오라클 specific 모델에 비해 대량의 작업일수록 속도 낮음
     OutofMemoryError를 방지하기 위해 executeBatch()를 중간 중간 날려주어야 한다.

ex)
    //..중간 생략
        m_conn = getConnection();

        SQLUtil sqlUtil = new SQLUtil( m_conn);

  try
  {
            PreparedStatement ps = sqlUtil.getPreparedStatement( new StringBuffer("INSERT INTO ").append( getTableName())
                         .append(" ( ")
                         .append( getColumns())
                         .append(") VALUES ")
                         .append( getInsertValue())
                         .toString());

            m_conn.setAutoCommit(false);

            int nDefault = 100;
            for( int i = 0; i < 10000; i++)
            {
                ps.setString( 1, "19001310095000" + ( nDefault + i));
                ps.setString( 2, "1310095");
                ps.setString( 3, "테스트");
                ps.setString( 4, "1");
                ps.setString( 5, "1900");

                // .. 중간 생략
               
                //Insert구문을 더한다.
                ps.addBatch();

                // 배치에 너무 많은 건수를 입력할 경우
                // OutofMemoryError가 발생하기 때문에 적당하게 나누어 주어야 한다.
                if( i % 5000 == 0) {
     ps.executeBatch();
    }
    //System.out.println(parent);
   }
   ps.executeBatch(); // 나머지 배치처리

            //  executeBatch의 리턴값은 int[]이다. 각각의 int값은
            // "0" 이상 -- 정상적인 처리 :  테이블의 변경된 행수를 나타낸다.
            // "-2" -- 정상적인 처리, 변경된 행수가 불분명하다.
            // "-3" -- 실행 실패,

            m_conn.commit();
  }
        catch(Exception e)
        {
            m_conn.rollback();
            e.printStackTrace();
        }
  finally
  {
   sqlUtil.close();
        }
    // .. 중간 생략

 

* Oracle-specific model(8.1.5이상)

장 : 대량의 작업일수록 퍼포먼스 향상
단 : 이식성떨어짐(오라클DB에서만 사용해야 함),
     적절한 배치크기를 설정해 주어야 함.
     conn.setDefaultExecuteBatch( 500) & ps.setExecuteBatch( 500);
     이 값들을 적절히 설정해야만 최상의 퍼포먼스를 얻을 수 있다.

특징 : 배치에 대한 어떠한 세팅도 이루어져 있지 않다면 connection은
      자동으로 1건데이터에 대한 각각의배치처리를 시도하려고 한다.
       오라클에서 이야기하는 최상의 퍼포먼스값은 batch작업의 크기가 5에서 30사이일때라고 이야기한다.
      (? 실제로는 더 큰 값을 주었을 때 성능향상이 있었다. 어찌 된일이지...쩝 , 아는 사람 알려주~~~)

    PreparedStatement에 아무리 열나게 배치를 올리고 있다고 하더라도 COMMIT만 만나면 driver는
    아무런 생각없이 바로 sendBatch()라는 request를 데이터베이스로 날려서 현재 올려진 데이터를 update시켜버린다.

ex)

    // ..중간 생략
        OracleConnection conn = null;
        OraclePreparedStatement ps = null;

        try
        {
            conn = ( OracleConnection)getConnection();

            conn.setAutoCommit(false);

            conn.setDefaultExecuteBatch( 500);

            ps = ( OraclePreparedStatement)conn.prepareStatement( new StringBuffer("INSERT INTO ").append( .append( getTableName())
                          .append(" ( ")
                          .append( getColumns())
                          .append(") VALUES ")
                          .append( getInsertValue())
                          .toString());

            ps.setExecuteBatch( 500);

            int nDefault = 100;

            for( int i = 0; i < 10000; i++)
            {
                ps.setString( 1, "19001310095000" + (nDefault + i));
                ps.setString( 2, "1310095");
                ps.setString( 3, "테스트");
                ps.setString( 4, "1");
                ps.setString( 5, "1900");
                               
                //.. 중간생략

                //이 부분에서는 DB로 아무값도 날아가지 않는다고 한다..좀 미심쩍긴 하지만 믿어야지 뭐....
                ps.executeUpdate ();

            }

            //단 한번의 roundtrip으로 작업이 수행된다.
            ps.sendBatch ();

            conn.commit();
        }
        catch( Exception e)
        {
            conn.rollback();
            e.printStackTrace();
        }
        finally
        {
            try
            {
                if( ps != null)
                {
                    ps.close();
                }

                if( m_conn != null)
                {
                    conn.close();
                }

            }
            catch( Exception e)
            {
                e.printStackTrace();
            }
        }

        ..중간 생략

그렇다면 OracleConnection, OraclePreparedStatement 이런 놈들은 도대체 어디에 존재할까?
J2SE, J2EE, JDBC2.0 API다 뒤져보아도 없다..
어디에 있을까?
답은 바로 우리가 흔히 쓰는 classes12.zip 요놈 안에 있다.
만약 오라클을 설치 하였다면 설치디렉토리\SID\JDBC 디렉토리에 가면 demo와 doc폴더에서 참조할 수 있다.

!! 주의사항 !!
JDBC2.O 과 Oracle-specific model를 동시에 사용할 수는 없다..

~~ 실제로 테스트 결과 : 데이터 건수가 많아질수록,
   Oracle-specific model 배치의 크기를 적절하게 주었을 때 좋은 성능을 볼수 있었다.