728x90
고아 데이터 이관 시 처리 방법?
고아 데이터(Orphaned Data) 이관 시 처리 방식은 데이터의 성격과 시스템 요구사항에 따라 다르지만, 일반적으로 다음과 같은 방식으로 처리됩니다:
✅ 고아 데이터란?
고아 데이터는 부모 엔티티와의 연관관계가 끊어진 자식 데이터를 의미합니다. 예를 들어, 부모 테이블의 레코드가 삭제되었는데도 불구하고, 자식 테이블에 해당 레코드에 대한 참조가 남아있는 경우가 이에 해당합니다.
🔍 고아 데이터 이관 시 처리 방법
1. 고아 객체 제거 (Orphan Removal)
-
JPA 등 ORM 프레임워크에서는 orphanRemoval = true 옵션을 통해 부모 엔티티에서 자식 엔티티의 참조를 제거하면, 해당 자식 데이터를 자동으로 삭제할 수 있습니다.
-
이 방식은 데이터 정합성을 유지하면서도, 불필요한 데이터를 제거하는 데 유용합니다.
-
예: parent.getChildList().remove(child) 시, DB에서도 자동 삭제됨.
2. 영속성 전이(Cascade) 설정
-
CascadeType.REMOVE 또는 CascadeType.ALL을 설정하면, 부모 엔티티 삭제 시 자식 엔티티도 함께 삭제됩니다.
-
이는 고아 데이터 발생 자체를 방지하는 방식입니다.
3. 이관 전 정리 작업
-
데이터 이관 전에 고아 데이터를 탐색하고 정리하는 프로세스를 거칩니다.
-
SQL 쿼리를 통해 부모 참조가 없는 자식 데이터를 찾아 삭제하거나, 별도의 보관 테이블로 이관할 수 있습니다.
4. 정책 기반 처리
-
일부 시스템(예: IBM BAW)에서는 고아 토큰을 관리하기 위한 마이그레이션 정책 파일을 생성하여, 이관 시 고아 데이터를 자동으로 삭제하거나 재할당하는 방식을 사용합니다.
⚠️ 주의사항
-
외래키 무결성 제약이 걸려 있는 경우, 부모 없이 자식 데이터만 남아 있으면 삭제나 이관 시 오류가 발생할 수 있습니다.
-
따라서 이관 전 제약 조건을 일시적으로 해제하거나, 순서에 맞게 데이터를 처리해야 합니다.
✅ 요약
Table
처리 방식설명적용 시점
| orphanRemoval=true | 부모와 연관 끊긴 자식 자동 삭제 | JPA 엔티티 설계 시 |
| CascadeType.REMOVE | 부모 삭제 시 자식도 삭제 | JPA 엔티티 설계 시 |
| 사전 정리 | 고아 데이터 탐색 및 삭제 | 이관 전 |
| 정책 파일 기반 처리 | 고아 데이터 정책 적용 | 이관 도구 또는 프레임워크 지원 시 |
필요하다면, 실제 시스템 환경(JPA, DB, 이관 도구 등)에 맞춰 구체적인 처리 방식을 설계해드릴 수 있습니다.
Share
실제 시스템 환경(JPA, DB, 이관 도구 등)에 맞춰 구체적인 처리 방식을 설계해줘
Edit
Share
아래는 “실제 운영 중인 시스템”을 기준으로
-
JPA (Spring Data JPA 3.x)
-
PostgreSQL 15
-
이관 도구: Flyway + 배치 Job (Spring Batch 5)
를 사용한다는 전제로 고아(Orphan) 데이터를 0%로 만들면서 이관하는 End-to-End 설계서입니다.
(Oracle, MySQL으로 DB를 바꿔도 구조는 동일합니다.)
1. 목표 정의
Table
항목값
| 고아 허용률 | 0% (이관 후 FK로 검증 시 0건) |
| 다운타임 | 30분 이내 (Blue-Green 배포 기준) |
| 롤백 가능 시점 | 이관 배치 실패 시 5분 이내 원복 |
| 보존 기간 | 삭제 전 30일간 보관 테이블 보관 |
2. 도메인 모델 예시
java
@Entity
@Table(name = "orders")
public class Order {
@Id private Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
}
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", foreignKey = @ForeignKey(name = "fk_item_order"))
private Order order;
}
-
orphanRemoval=true → 운영 코드에서 이미 고아 발생 차단
-
그러나 이관 대상은 기존 레거시 데이터이므로 별도 정리 필요
3. 이관 전 체크리스트 (Pre-Migration)
3-1. 고아 탐지 SQL (PostgreSQL 기준)
sql
-- 1) order_items에 order_id가 없는 레코드
SELECT item.*
FROM order_items item
LEFT JOIN orders o ON o.id = item.order_id
WHERE o.id IS NULL;
-- 2) FK가 DISABLE돼 있을 수 있으므로 추가 검증
SELECT conname
FROM pg_constraint
WHERE contype = 'f'
AND conname = 'fk_item_order'
AND NOT convalidated;
결과 건수 = 0 이어야 함.
0건이 아니면 4단계 “고리 제거” 실행
3-2. 운영 중단점 결정
-
Blue-Green 배포 → Green 환경으로 트래픽 스위치 직전에 마지막 증분 데이터 적재
-
증분 구간: modified_date < SWITCHOVER_TIME - 5min (5분 버퍼)
4. 고리 제거 배치 (OrphanCleanupJob)
Spring Batch Job 하나를 만들어 이관 전날 밤 실행.
Table
Step설명SQL/코드
| 1 | 고아 백업 | CREATE TABLE order_items_orphan_bak AS SELECT * FROM order_items WHERE order_id NOT IN (SELECT id FROM orders); |
| 2 | 고아 삭제 | DELETE FROM order_items WHERE order_id NOT IN (SELECT id FROM orders); |
| 3 | FK 재활성화 | ALTER TABLE order_items VALIDATE CONSTRAINT fk_item_order; |
| 4 | 건수 기록 | INSERT INTO migration_log(table_name, orphan_cnt, deleted_at) VALUES ('order_items', @@rowcount, now()); |
-
청크 크기: 10,000 (대용량 시 커서 기반)
-
트랜잭션: 1 청크당 1 트랜잭션 → 실패 시 재시도 3회
-
스킵 정책: DeadLock 발생 시 50ms 지연 후 3회 재시도
5. 스키마 이관 (Flyway)
V202510230100__baseline.sql
-
Green 환경 신규 스키마 생성
-
FK는 모두 DEFERRABLE INITIALLY DEFERRED 로 생성
→ 데이터 적재 중 일시적 무순서 허용, 커밋 직전 검증
6. 데이터 이관 배치 (MigrateMainJob)
Table
Step기능비고
| 1 | Source 테이블 → CSV DUMP | COPY (SELECT * FROM orders WHERE modified_date <= :cutoff) TO '/data/orders.csv' CSV; |
| 2 | Green DB 적재 | COPY orders FROM '/data/orders.csv' CSV; |
| 3 | 아이템 적재 | 동일 |
| 4 | FK 검증 | SET CONSTRAINTS ALL IMMEDIATE; → 실패 시 롤백 |
| 5 | 시퀀스 동기화 | SELECT setval('orders_id_seq', max(id)) FROM orders; |
-
전체 Step은 JobRepository에 결과 저장 → 실패 시 Slack 알림 + 자동 롤백
-
청크 기반 병렬 처리: 4 Thread, 1 청크 = 5,000건
7. 검증 (ValidationJob)
sql
-- 1) 고아 재확인
SELECT COUNT(*)
FROM order_items oi
LEFT JOIN orders o ON o.id = oi.order_id
WHERE o.id IS NULL;
-- 2) Row count 비교
SELECT 'orders', count(*) FROM orders
UNION ALL
SELECT 'order_items', count(*) FROM order_items;
-
결과를 검증 테이블에 저장 → 0건이면 SUCCESS, 1건 이상이면 FAIL
8. 롤백 플랜
-
Green 환경 교체 전 → Blue 그대로 유지
-
실패 시 Green DB Drop → Blue 유지 → 5분 내 트래픽 복구
-
보관 테이블(*_orphan_bak)은 30일 후 VACUUM으로 물리 삭제
9. 자동화 흐름 (CI/CD)
bash
1. git tag: release/1.2.0
2. Jenkinsfile
- stage: flyway-migrate-green
- stage: orphan-cleanup-job (Spring Batch 호출)
- stage: migrate-main-job
- stage: validation-job
- stage: switch-traffic (k8s patch)
3. 실패 시 자동 롤백 스크립트 실행
10. 운영 이후
-
JPA Entity는 이미 orphanRemoval=true 이므로 신규 데이터에 대해선 고아 0% 유지
-
매주 일요일 FK 검증 리포트를 Jenkins 로 자동 생성 → 0건이 아닐 경우 PagerDuty 알림
11. 필요 스크립트 모음 (Git 레포 구조)
migration/
├─ flyway/
│ └─ sql/
├─ batch/
│ ├─ OrphanCleanupJob.java
│ ├─ MigrateMainJob.java
│ └─ ValidationJob.java
└─ scripts/
├─ detect_orphan.sh
├─ rollback_green.sh
└─ report_weekly.sh
12. 한 줄 요약
“이관 전날 밤 고아를 탐지→백업→삭제→FK 재활성화하고,
이관 당일 증분 데이터를 Blue-Green으로 스위치 직전에 병렬 적재+FK 검증하며,
실패 시 5분 내 Blue로 롤백하는 Spring Batch 기반 무고아(0%) 이관 파이프라인”
이관 당일 증분 데이터를 Blue-Green으로 스위치 직전에 병렬 적재+FK 검증하며,
실패 시 5분 내 Blue로 롤백하는 Spring Batch 기반 무고아(0%) 이관 파이프라인”
필요하면 위 Job 클래스 소스코드나 Flyway SQL, Helm chart까지 제공해드릴 수 있습니다.
728x90
'정보관리(데이터베이스, DB) > DB' 카테고리의 다른 글
| tibero에서 테이블에 데이터가 입력되는 시간 또는 순서를 기준으로 추적하고 싶다. 쿼리문으로 정보를 추적할 수 있나? (0) | 2025.10.27 |
|---|---|
| DATE형-VARCHAR (0) | 2025.10.23 |
| DBMS/SQL[데이터베이스] 무결성 제약 조건 (Integrity Constraints : IC) (0) | 2025.10.23 |
| [Oracle] 오라클 11g 접속 오류 및 설치 삭제 / Oracle Database XE 21c, 19c 설치 / 스키마 및 사용자 생성 / SQL Developer 설치 (0) | 2025.10.16 |
| [Oracle] ORA-01034: ORACLE not available 오류 해결법 (0) | 2025.10.16 |