4. 응용 변형 예제
예제 6: DB별 청크 문법 (문법 검토만)
N건만 처리하는 문법은 DB 마다 다릅니다. 아래 문장은 이 레슨에서 H2 로 실행하지 않았고 문법 검토만 했습니다. 처리 표시 컬럼으로 미처리 1,000건을 갱신하는 예입니다.
-- Oracle · Tibero
UPDATE target
SET processed_yn = 'Y'
WHERE processed_yn = 'N'
AND ROWNUM <= 1000;Oracle 12c 부터는 예제 4 처럼 FETCH FIRST 를 서브쿼리에 쓸 수도 있습니다. MySQL 은 UPDATE 에 LIMIT 을 바로 씁니다.
-- MySQL
UPDATE target
SET processed_yn = 'Y'
WHERE processed_yn = 'N'
ORDER BY id
LIMIT 1000;MSSQL 은 UPDATE 뒤에 TOP 을 씁니다.
-- MSSQL
UPDATE TOP (1000) target
SET processed_yn = 'Y'
WHERE processed_yn = 'N';MSSQL 의 UPDATE TOP 은 어떤 행이 뽑히는지 정해 주지 않습니다. 순서가 필요하면 키를 골라 조인하는 식으로 씁니다. 청크 크기는 잠금 에스컬레이션이 생기는 약 5,000개보다 작게 두기도 합니다.
예제 7: JDBC 배치와 배치 커밋
애플리케이션에서 여러 건을 넣을 때는 JDBC 배치를 씁니다. setAutoCommit(false) 로 자동 커밋을 끄고, addBatch 로 모아 1,000건마다 executeBatch 와 commit 을 합니다. 마지막 잔여분도 처리하고, 예외가 나면 rollback 합니다.
String sql = "INSERT INTO target (id, amt) VALUES (?, ?)";
try (Connection con = ds.getConnection();
PreparedStatement ps = con.prepareStatement(sql)) {
con.setAutoCommit(false);
try {
int n = 0;
for (Row r : rows) {
ps.setInt(1, r.id());
ps.setInt(2, r.amt());
ps.addBatch();
if (++n % 1000 == 0) {
ps.executeBatch();
con.commit();
}
}
ps.executeBatch(); // 남은 잔여분
con.commit();
} catch (SQLException e) {
con.rollback(); // 마지막 커밋 이후만 취소
throw e;
}
}executeBatch 는 모아 둔 문장을 한 번에 보내 왕복을 줄입니다. rollback 은 마지막 commit 이후의 청크만 취소하므로, 앞선 청크는 남습니다. 그래서 재시작 지점과 멱등성이 필요합니다.
MySQL 은 JDBC URL 에 rewriteBatchedStatements=true 를 붙이면 배치를 여러 행 INSERT 로 합쳐 보냅니다. MyBatis 는 SqlSession 을 ExecutorType.BATCH 로 열면 같은 방식으로 배치가 됩니다.
예제 8: 청크 흐름과 대량 적재 도구 (도식과 문법 검토만)
나눠서 처리하는 반복문의 흐름은 아래와 같습니다.
도식(H2 미지원, 실행 결과 아님)
시작 → 진행 표에서 last_id 읽기
↓
청크 처리: id > last_id AND id <= last_id + N
↓
처리 건수 0 이면 종료
↓
last_id 갱신 → COMMIT → 다음 청크실패하면 프로그램을 다시 시작해 진행 표의 last_id 부터 이어 갑니다. 이미 커밋된 청크는 남아 있고, 실패한 청크는 롤백되어 처음부터 다시 처리됩니다.
수백만 건 이상의 초기 적재는 SQL 문장보다 전용 도구가 빠릅니다. 아래는 문법 검토만 한 예입니다.
-- Oracle · Tibero: 직접 경로 INSERT
INSERT /*+ APPEND */ INTO target
SELECT * FROM staging;
COMMIT;-- MySQL
LOAD DATA INFILE '/data/staging.csv' INTO TABLE target
FIELDS TERMINATED BY ',';
-- MSSQL
BULK INSERT target FROM 'C:\data\staging.csv'
WITH (FIELDTERMINATOR = ',', TABLOCK);Oracle 직접 경로 INSERT 는 표를 잠그고, 커밋 전에는 같은 세션에서도 그 표를 조회할 수 없습니다. NOLOGGING 과 함께 쓰면 복구에 주의가 필요합니다. MSSQL 은 복구 모델이 SIMPLE 또는 BULK_LOGGED 이고 TABLOCK 을 쓰면 최소 로깅이 됩니다. Oracle PL/SQL 에는 BULK COLLECT 와 FORALL 이 있다는 정도만 알아 둡니다.