헤르메스 LIFE

[iBatis] iBatis에서 batch 기능 활용하기 본문

Spring Framework

[iBatis] iBatis에서 batch 기능 활용하기

헤르메스의날개 2011. 1. 20. 14:05
728x90

원문 : http://fantazic.com/page/47

iBatis는 아래와 같은 방법으로 batch 처리가 가능하다. iBatis 내부 코드를 확인해 본 바로는 PreparedStatement.addBatch()를 사용하고 있고, 동일한 쿼리가 반복해서 들어올 때 하나의 batch로 처리해준다.

try {
  SqlMapClient.startTransaction();
  SqlMapClient.startBatch();
  while (...) {
    SqlMapClient.insert(query, params);
  }
  SqlMapClient.executeBatch();
  SqlMapClient.commitTransaction();
} catch (Exception e) {
  log.error(e, e);
} finally {
  SqlMapClient.endTransaction();
}

이 기능을 활용해서 BatchManager를 만들어서 사용하고 있는데, 사용자의 로그인 시간을 기록하거나 게시물의 조회수를 늘리는 등 빈번하게 동일한 update가 발생하는 서비스에 사용하면 효과가 있다.

사용법은 기존의 서비스 코드 수정을 최소화하는 방법으로 고안했다. SqlMapClient.insert(query, params)를 BatchManager.insert(query, params)로 수정하면 된다.

관련 코드)
BatchManager.java

public class BatchManager {
  private static BatchWorker worker = BatchWorker.getInstance();
  static {
    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          worker.flushAll();
          worker.stop();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }

  public static void insert(String query, Object params) {
    worker.put(new BatchQuery(INSERT, query, params));
  }

  public static void update(String query, Object params) {
    worker.put(new BatchQuery(UPDATE, query, params));
  }
}

BatchWorker.java

public class BatchWorker {
  public static final int HEARTBEAT = 1000;
  public static int MAX_WAIT = 30000;
  public static final int SIZE_OF_ONE_BATCH = 200;
  public static final int MAX_SIZE = 100;
  private long lastTime;
  private final Timer timer;
  private Vector queue;
  private static BatchWorker singletonWorker;

  private BatchWorker() {
    lastTime = System.currentTimeMillis();
    timer = new Timer(true);
    queue = new Vector();
    startWorker();
  }

  public static synchronized BatchWorker getInstance() {
    if (singletonWorker == null)
      singletonWorker = new BatchWorker();
    return singletonWorker;
  }

  private void startWorker() {
    timer.scheduleAtFixedRate(new TimerTask() {
      @Override
      public void run() {
        try {
          doBatch();
        } catch (Exception e) {
          // ignore
          e.printStackTrace();
        }
      }
    }, 0, HEARTBEAT);
  }

  private void doBatch() {
    if (System.currentTimeMillis() - lastTime > MAX_WAIT
      || queue.size() >= MAX_SIZE) {
      executeQuery();
      lastTime = System.currentTimeMillis();
    }
  }

  private synchronized void executeQuery() {
    if (queue.size() == 0)
      return;
    try {
      int cnt = 0;
      SqlMapClient.startTransaction();
      SqlMapClient.startBatch();
      while (cnt++ < SIZE_OF_ONE_BATCH) {
        if (queue.size() == 0)
          break;
        BatchQuery batchQuery = queue.remove(0);
        switch (batchQuery.type) {
          case INSERT:
            SqlMapClient.insert(batchQuery.query, batchQuery.params);
            break;
          case UPDATE:
            SqlMapClient.update(batchQuery.query, batchQuery.params);
            break;
          default:
            break;
        }
      }
      SqlMapClient.executeBatch();
      SqlMapClient.commitTransaction();
    } catch (Exception e) {
      log.error(e, e);
    } finally {
      SqlMapClient.endTransaction();
    }
  }

  public int size() {
    return queue.size();
  }

  public void put(BatchQuery query) {
    queue.add(query);
  }

  public void flushAll() {
    while (queue.size() > 0)
      executeQuery();
  }

  public void stop() {
    timer.cancel();
  }
}

BatchQuery.java

public class BatchQuery {
  public enum QueryType {
    INSERT, UPDATE
  }

  public String query;
  public Object params;
  public QueryType type;

  public BatchQuery(QueryType type, String query, Object params) {
    this.type = type;
    this.query = query;
    this.params = params;
  }
}

참고)

  • Oracle10g 환경에서는 batch로 처리할 경우 쿼리 수행은 빨라지나 batch 처리마다 쿼리 파싱이 발생해서 CPU 비용은 증가하는 경우도 보였다.
  • 예전에 찾아본 바로는 한번에 만건 이상도 batch 처리가 가능하다고 한다. 환경에 따라 가장 효율적인 batch 크기를 결정해야 한다.
  • batch 처리할 경우 수행속도가 빨라지는 장점이 있고 transaction lock이 적게 잡혀 DB 부담을 줄여주는 효과도 있다.

728x90