제목 : [ORACLE] 대규모 데이터 마이그레이션에서 인덱스 비활성화가 가져다주는 이점
⚠️ 운영 환경에서 대규모 마이그레이션 시 발생하는 문제
Oracle DB에서 기존 마켓 테이블의 이미지 여러 컬럼(IMAGE_URL1 ~ 4)로 구성된 데이터를 No 체계로 별도의 테이블로 관리하고 마켓 테이블에서는 IMAGE_NO를 마이그레이션하여 별도의 이미지 관리 테이블에서 URL을 관리하도록 구조 변경을 진행할 때 대규모 UPSERT 작업이 필요했습니다.
처음 배치를 통해 점진적 마이그레이션을 진행하려 했지만, 내부 의견으로는 번거로우니 서비스 중단 후 한번에 작업하자
로 결정되어 빅뱅 패치를 진행하게 되었습니다.
운영 환경에서 한 번에 모든 데이터를 마이그레이션하다 보니, 개발 환경과 비교해서 처리 속도가 급격히 느려지거나 시스템 자원(CPU, I/O, 메모리) 경합이 발생하는 문제가 관찰되었습니다.
"MVCC로 인해 전혀 영향이 없는 SELECT만 하는 쿼리가 몇개 실행되는 정도 인데도, 마이그레이션 작업이 이렇게까지 느려질 수 있나?" 하는 의문을 갖게 되었고, 자세히 살펴보니 다음과 같은 원인들을 발견할 수 있었습니다.
👌문제 찾기
이슈로는 다음과 같은 현상이 있었어요:
- 대규모 UPSERT/INSERT 작업 시 DB 서버 부하가 급격히 증가
- 기존 서비스에서 SELECT만 하고 있음에도, 전체 성능이 저하
- 인덱스가 걸린 컬럼에 대해서는 UPDATE/INSERT가 일어날 때마다 인덱스를 유지해야 하므로 트랜잭션 시간이 길어짐
특히, 한 번에 대규모로 데이터를 옮기다 보니 언두(Undo) 및 리두(Redo) 로그 생성량이 폭증하고, 인덱스를 유지하는 데 드는 비용이 기하급수적으로 커졌습니다. 그렇다면 대규모 DML 시 인덱스를 어떻게 다뤄야 할까요?
💣 인덱스 유지 비용과 문제 시나리오
Oracle은 MVCC(Multi-Version Concurrency Control)를 사용해 SELECT가 직접 블로킹을 일으키지는 않습니다. 그렇지만, 아래와 같은 시나리오에서 성능 저하가 가속화될 수 있습니다.
한 번에 너무 많은 데이터를 UPSERT
- 대량 트랜잭션으로 인해 Undo/Redo 로그가 급증
- Redo 로그 스위치가 매우 자주 발생하여 I/O 부하 증가
여러 인덱스가 있는 테이블에 대량 Insert/Update
- 각각의 인덱스를 실시간으로 갱신해야 하기 때문에 추가 성능 부담
- 인덱스가 많을수록 작업 시간이 지연
서비스 쿼리 & 마이그레이션 쿼리 동시 사용
- 리소스(CPU, 메모리, I/O)에 대한 경합
- 언두 영역 부족 시 성능 저하 가속
결과적으로, 인덱스가 많은 테이블에 대량의 DML이 수행되면 각 인덱스를 모두 갱신해야 하므로 작업 시간이 크게 늘어납니다.
✅ 문제해결: 인덱스 비활성화 전략
이 문제를 해결하기 위한 핵심 아이디어 중 하나는, 마이그레이션 시점에 “불필요하게” 인덱스를 유지할 필요가 없다면 인덱스를 일시적으로 비활성화했다가, 작업이 완료된 후에 다시 활성화(재생성)하는 것입니다.
인덱스 비활성화 (UNUSABLE 혹은 DROP)
- “ALTER INDEX 인덱스명 UNUSABLE” 명령을 통해 인덱스를 사용할 수 없게 설정
- 혹은 “DROP INDEX 인덱스명”으로 아예 제거 후 마이그레이션이 끝나면 다시 CREATE INDEX
- UNIQUE/PRIMARY KEY 인덱스, FK 제약이 있는 경우 주의(제약 비활성 → DML → 제약 재활성화)
대량 DML 수행
- 테이블에 대량 UPSERT/INSERT/UPDATE 작업을 진행
- 인덱스 유지 비용이 없으므로 트랜잭션 시간이 단축됨
- Undo/Redo 처리량도 감소 (단, 데이터량이나 병렬처리 방식에 따라 차이가 있을 수 있음)
인덱스 재생성(혹은 REBUILD)
- 작업 완료 후 “ALTER INDEX 인덱스명 REBUILD” 또는 “CREATE INDEX …”를 통해 인덱스를 다시 생성
- 병렬(PARALLEL) 옵션 활용 시 재생성 시간을 단축할 수 있음
📈 운영 환경에서의 영향
인덱스 비활성화 후 마이그레이션을 진행하면 다음과 같은 효과를 기대할 수 있습니다:
성능 향상
- 대량 DML 시 인덱스 유지 비용이 제거되므로 트랜잭션 처리 속도가 빨라짐
- DB I/O 부하 감소로 인해 전체 운영 서비스도 방해를 덜 받음
배치 작업 시간 단축
- 마이그레이션 작업량이 크더라도, 인덱스가 없는 상태에서는 데이터 적재 속도가 빨라 더 짧은 시간 내에 완료
장애(또는 에러) 발생 시 복구 부담 완화
- 부분적으로 작업을 나누어 진행하고, 각 배치 단위에 대해 커밋 관리가 쉬워짐
- 인덱스를 미리 Drop/Unusable 상태로 두면, 잘못된 인덱스 유지로 인한 에러 가능성 최소화
다만, 인덱스가 UNUSABLE 상태일 때 해당 인덱스를 사용하는 SELECT 쿼리가 있으면 오류가 발생할 수 있으니, 운영 중이라면 쿼리 패턴 분석, 장애 시간 고려가 필수입니다.
🔧 권장 사항
배치 단위로 작업 분할
- 한꺼번에 모든 행을 처리하기보다는 적절한 범위(예: 몇 만 건 단위)로 나누어 작업
- 트랜잭션 크기가 줄어들어 언두 및 로그 부하 분산
인덱스 비활성화(드롭) 후 마이그레이션
- FK 및 UNIQUE 제약이 있는 경우 제약을 먼저 풀어야 하는지 사전 조사 필요
- 마이그레이션 완료 후 인덱스를 병렬로 재생성(Rebuild)
서비스 영향 최소화
- 리소스 사용량이 적은 시간(야간 등)에 배치 작업 실행
- 필요 시 리소스 제한을 걸어 병렬도(Parallel) 조절
작업 후 모니터링 및 튜닝
- AWR 리포트, V$SESSION, V$SQL 등의 성능 지표 확인
- 인덱스 재생성 후 정상적으로 SELECT에 활용되고 있는지 확인
📚 참고 자료
- Oracle Database 공식 문서
- [Effective Oracle Database 10g Design & Programming - Thomas Kyte]
- Oracle Index 사용 가이드: DROP vs UNUSABLE vs REBUILD
💡결론
운영 환경에서 대규모 마이그레이션을 진행할 때, 대량의 DML 연산에 의해 인덱스 유지 비용이 급격히 증가하는 것은 흔히 겪는 문제입니다. 특히 테이블에 여러 인덱스가 구성되어 있을수록 부담이 커집니다.
이를 해결하는 실질적인 방법은 “필요 없는 인덱스를 잠시 끄고(UNUSABLE, DROP) 나중에 재생성”하는 접근입니다. 이로써 마이그레이션 속도를 크게 높이고, 서비스 영향도 줄일 수 있습니다. 다만 운영 중 사용 중인 인덱스라면 충분한 사전 검토 및 작업 스케줄링을 통해 안정적으로 진행해야 하며, FK/UNIQUE와 같은 제약 사항은 미리 해제와 재설정을 고려해야 합니다.
Tip: 대량 마이그레이션에서 인덱스 비용을 최소화하는 것은 마이그레이션 시간, 장애 발생 리스크 모두를 줄이는 핵심 전략입니다. 사전에 이런 방법을 알았다면, 조금 더 문제가 없도록 처리가 가능했을 것 같아요. 😂
'Error > 트러블슈팅' 카테고리의 다른 글
[JAVA] ENUM에 setter를 쓰면 생기는 일 (feat. 싱글턴) (1) | 2024.12.10 |
---|---|
[Trouble Shooting - CORS] Spring Boot CORS 와일드카드(*) 설정방법 (0) | 2023.07.22 |
[Trouble Shooting - Transaction과 DB Session] DB 특정 테이블 (row) 업데이트 안되는 문제 (0) | 2023.07.17 |
[Trouble Shooting - RequestBody와 생성자] 스프링부트 LocalDateTime JsonFormat 안됨 (2) | 2023.05.19 |