Unity Catalog Volume 은 /Volumes/<catalog>/<schema>/<volume>/... 형태의 POSIX 경로로 접근되므로, 카탈로그나 스키마가 달라도 같은 metastore 안이면 그냥 복사하면 된다. recurse=True 는 하위 폴더 구조를 그대로 유지한다.
src = "/Volumes/cat_a/sch_a/vol_a"
tgt = "/Volumes/cat_b/sch_b/vol_b"
dbutils.fs.cp(src, tgt, recurse=True)
파일이 수만 개 이상으로 많아지면 한 번에 돌리지 말고 최상위 디렉터리 단위로 나눠 돌리는 편이 안전하다. 진행 상황도 보이고 실패 시 다시 시작할 지점이 분명해진다.
for d in dbutils.fs.ls(src):
dbutils.fs.cp(d.path, f"{tgt}/{d.name}", recurse=True)
print("done:", d.name)
일회성 노트북 실행이라면 dbutils.widgets 를 쓸 필요 없이 경로를 상수로 두는 편이 단순하다.
단순 복사가 아니라 이관 전략을 먼저 정해야 한다. metastore 관계가 답의 절반을 결정한다.
| 대상 | 방법 |
|---|---|
| 테이블 | Delta Sharing 으로 공유한 뒤 받은 쪽에서 CTAS 로 물리 복사 |
| Volume 파일 | 스토리지 레벨 cross-region 복사(aws s3 sync) 가 가장 빠르다 |
| 모델 | 레지스트리 export · import 또는 아티팩트 직접 복사 |
타깃 리전에는 미리 S3 버킷, Storage Credential, External Location, 작업용 Service Principal 을 준비해 둔다.
-- 양쪽에서 metastore ID 를 확인한다. recipient 등록에 필요하다
SELECT current_metastore();
-- 소스 워크스페이스
CREATE SHARE migration_share COMMENT 'one-time migration';
ALTER SHARE migration_share ADD TABLE src_catalog.src_schema.tbl_a;
-- 스키마 통째로: ALTER SHARE migration_share ADD SCHEMA src_catalog.src_schema;
CREATE RECIPIENT target_recipient USING ID 'aws:<target-region>:<target-metastore-id>';
GRANT SELECT ON SHARE migration_share TO RECIPIENT target_recipient;
테이블이 많으면 목록을 읽어 반복한다.
tables = [r.tableName for r in spark.sql("SHOW TABLES IN src_catalog.src_schema").collect()]
for t in tables:
spark.sql(f"ALTER SHARE migration_share ADD TABLE src_catalog.src_schema.{t}")
타깃 워크스페이스에서는 provider 로 등록된 share 를 카탈로그로 붙인 뒤 CREATE TABLE ... AS SELECT 로 물리 복사한다. Delta Sharing 은 데이터를 옮기지 않고 읽게 해 주므로, 일회성 마이그레이션이면 CTAS 까지 해야 진짜 이관이 된다.