홈 › 대용량·배치 › 06 / 8

대량 INSERT·UPDATE 와 배치 커밋

집합 처리, 나눠서 커밋, JDBC 배치
섹션 6진행 0 / 8

2. 핵심 원리

2.1 한 행씩 처리가 느린 이유

애플리케이션이 행마다 SQL 을 보내면 행마다 세 가지 비용이 듭니다. 네트워크 왕복, SQL 파싱과 계획 준비, 그리고 자동 커밋이 켜져 있다면 행마다 커밋입니다. 4,000건이면 이 비용이 4,000번 반복됩니다.

집합 처리는 같은 일을 DB 안에서 한 문장으로 처리합니다. 왕복과 파싱은 한 번이고 커밋도 한 번입니다. DB 는 여러 행을 묶어 효율적으로 다룰 수 있습니다.

방식 왕복 파싱 커밋
한 행씩(루프) 행마다 행마다 행마다(자동 커밋 시)
집합 한 문장 한 번 한 번 한 번
나눠서 처리 청크마다 청크마다 청크마다

2.2 집합 처리 도구

집합 처리는 세 가지 형태가 기본입니다. INSERT ... SELECT 는 원천 표를 읽어 대상 표에 한 번에 넣습니다. 집합 UPDATE 는 CASE 로 여러 규칙을 한 문장에 담습니다. 여러 행 VALUES 는 값을 직접 넣을 때 한 문장에 여러 행을 씁니다.

여러 행 VALUES 는 DB 별로 지원이 다릅니다.

DB 여러 행 VALUES
MySQL 지원
MSSQL 2008 부터, 한 VALUES 절에 최대 1000행
Oracle 23ai 부터 지원
Oracle 23ai 이전 INSERT ALL 또는 SELECT ... FROM DUAL UNION ALL

Oracle 23ai 이전의 INSERT ALL 과 UNION ALL 형태는 이 레슨에서 실행하지 않았고 문법 검토만 했습니다.

2.3 왜 나눠서 커밋하는가

한 트랜잭션이 너무 크면 세 가지 문제가 생깁니다. 언두·로그 공간이 커지고, 잠근 행이 커밋까지 오래 유지되어 다른 세션이 기다립니다. 실패하면 롤백이 오래 걸립니다.

너무 잘게 나눠도 문제입니다. 커밋마다 로그를 디스크에 쓰고 기다리므로 커밋 횟수가 늘수록 대기가 쌓입니다. 보통 1,000~10,000건 단위로 시작해 측정으로 조정합니다.

핵심

한 행씩은 너무 잘고 한 번에 통째는 너무 큽니다. 1,000~10,000건 정도로 나눠 청크마다 COMMIT 하고, 크기는 실제 환경에서 측정해 조정합니다.

2.4 중간 실패와 재시작

나눠서 커밋하면 실패했을 때 이미 커밋된 청크는 남습니다. 그래서 어디까지 했는지 알아야 하고, 이어서 다시 돌려도 같은 결과여야 합니다. 이 성질을 멱등성이라고 합니다.

재시작 지점을 남기는 방법은 두 가지입니다.

방법 기록하는 것 다시 시작할 때
마지막 키 기록 진행 표의 last_id id > last_id 부터 이어서
처리 표시 컬럼 행마다 processed_yn processed_yn = 'N' 인 행만

키가 연속적으로 커지는 표는 마지막 키가 간단합니다. 키가 듬성듬성하거나 여러 조건으로 골라 처리하면 처리 표시 컬럼이 안전합니다.

2.5 청크 문법과 대량 적재 도구

항목 Oracle · Tibero MySQL MSSQL
N건만 갱신 WHERE ROWNUM <= n, 서브쿼리 안 FETCH FIRST(12c) UPDATE ... LIMIT n UPDATE TOP (n)
대량 적재 직접 경로 INSERT, SQL*Loader LOAD DATA INFILE BULK INSERT, bcp
배치 합치기 드라이버 배치 rewriteBatchedStatements=true 드라이버 배치

MSSQL 은 한 문장이 약 5,000개가 넘는 잠금을 잡으면 표 전체 잠금으로 올리는 잠금 에스컬레이션이 있습니다. 그래서 청크를 그보다 작게 두기도 합니다. 락의 상세는 다음 레슨(락과 데드락)에서 다룹니다.