대용량 데이터를 마이그레이션하거나 백업 테이블을 생성할 때,
가장 많이 쓰는 SQL 문 중 하나가 바로 INSERT INTO ... SELECT입니다. 📥
하지만 데이터가 수십만~수백만 건에 달하면,
속도가 느려지고 트랜잭션 병목 현상이 생기기 시작하죠.
오늘은 실무에서 자주 쓰이는 INSERT SELECT 성능 최적화 방법을
정리해 드릴게요. 속도 차이를 체감할 수 있을 겁니다! ⚡
1️⃣ 기본 구문 복습
INSERT INTO target_table (col1, col2, ...)
SELECT col1, col2, ...
FROM source_table
WHERE 조건;
문법 자체는 간단하지만, 성능은 설계와 환경 설정에 따라 크게 달라집니다.
⚡ 2. INSERT SELECT 속도 향상 팁
✅ (1) Direct-Path Insert 사용
INSERT /*+ APPEND */ INTO target_table
SELECT * FROM source_table;
- APPEND 힌트를 사용하면 Direct-Path Insert가 실행되어
버퍼 캐시를 거치지 않고 바로 데이터 파일에 기록됩니다. - 단, 이 방법은 로그를 적게 쓰고 빠르지만,
UNDO 사용이 제한되며 병렬처리나 제약조건 주의가 필요합니다.
📌 주의: Direct-Path는 테이블이 NOLOGGING 또는 LOGGING 조건에 따라
Redo log에 기록되지 않을 수 있으니 복구 계획이 있을 경우 유의하세요.
✅ (2) 병렬 처리(PARALLEL) 힌트 추가
INSERT /*+ APPEND PARALLEL(tgt, 4) */ INTO target_table tgt
SELECT /*+ PARALLEL(src, 4) */ *
FROM source_table src;
- PARALLEL(table_alias, degree) 힌트로 동시 처리 스레드 수 지정 가능
- 소스와 타겟 테이블 모두 병렬 설정 가능
- 병렬 옵션은 CPU 리소스를 많이 사용하므로 서버 상태를 고려해야 합니다
✅ (3) 제약조건, 인덱스 일시 제거
- INSERT 대상 테이블에 많은 제약조건(FK, CHECK)이나 인덱스가 있으면 느려집니다
- 가능하면 아래처럼 처리하세요
-- 인덱스 비활성화
ALTER INDEX index_name UNUSABLE;
-- 제약조건 비활성화
ALTER TABLE table_name DISABLE CONSTRAINT constraint_name;
💡 작업 완료 후에는 반드시 인덱스와 제약조건을 다시 활성화해야 합니다!
✅ (4) 자동 커밋 방지 + 트랜잭션 쪼개기
- 대용량 INSERT 시 트랜잭션이 커지면 성능이 급격히 저하
- 가능하면 작게 나눠서 COMMIT을 주기적으로 수행
-- 예: PL/SQL 루프 내 1000건마다 COMMIT
FOR rec IN (
SELECT * FROM source_table
) LOOP
INSERT INTO target_table VALUES (rec.col1, rec.col2, ...);
i := i + 1;
IF MOD(i, 1000) = 0 THEN
COMMIT;
END IF;
END LOOP;
COMMIT;
📊 요약 정리
방법 | 설명 |
/*+ APPEND */ | Direct-Path Insert 사용으로 속도 향상 |
/*+ PARALLEL */ | 병렬 처리로 대용량 데이터 동시 삽입 |
인덱스/제약 제거 | INSERT 중 리소스 낭비 줄이기 |
트랜잭션 분할 | Out-of-memory 방지 및 속도 개선 |
UNDO/LOGGING 설정 | 복구 가능성과 성능의 균형 고려 필요 |
✅ 마무리 요약
대량의 데이터를 빠르게 삽입하려면 단순 INSERT SELECT만으론 부족합니다.
APPEND, PARALLEL 같은 힌트를 활용하고, 제약조건·트랜잭션 구조를
현명하게 설계하면 작업 속도를 몇 배 이상 향상시킬 수 있어요! 🚀
단, 성능을 올리는 만큼 데이터 무결성과 복구 전략도 함께 고려하는 게 중요합니다.
빠르게 넣는 것도 좋지만, 안전하게 넣는 방법도 꼭 기억하세요.