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; } }
참고)
Your email is never published nor shared. Required fields are marked *
You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
iBatis에서 batch 기능 활용하기
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; } }참고)