지난 편에서는 누가 데이터를 볼 수 있는지를 다뤘습니다. 이번 편에서는 그 데이터가 어디 있는지를 다룹니다. 'US 멀티리전에 두면 여러 리전에 복제되니까 안전하다'는 말부터 틀렸습니다.
1. 멀티리전이라는 오해
1-1. US 멀티리전 데이터는 한 리전에만 있다
BigQuery의 location은 두 종류입니다. us-central1(아이오와), asia-northeast3(서울) 같은 단일 리전과, US, EU 같은 멀티리전입니다.
멀티리전이라는 이름 때문에 '여러 리전에 데이터가 복제된다'고 생각하기 쉽지만, 실제 동작은 다릅니다. 공식 문서 표현 그대로, 멀티리전을 선택한다는 건 그 지리적 영역 안에서 데이터를 저장할 단일 리전을 BigQuery가 고르게 한다는 뜻입니다.
US 멀티리전을 선택하면:
→ us-central1(아이오와), us-west1(오리건), us-central2(오클라호마)
중 한 곳에 저장. 어디인지는 BigQuery가 결정
EU 멀티리전을 선택하면:
→ europe-west1(벨기에), europe-west4(네덜란드) 중 한 곳에 저장문서는 한발 더 나가서 명시합니다. 멀티리전 선택은 리전 간 복제도, 리전 이중화도 제공하지 않습니다. 리전 장애가 나도 가용성은 그대로입니다. 컴퓨트도 데이터가 있는 그 리전 안에서만 돕니다.
멀티리전의 실익은 단일 리전보다 큰 쿼터를 받는다는 것 정도입니다. 가용성 SLA는 리전이든 멀티리전이든 똑같이 99.99%라서, '멀티리전이니까 더 안전하다'는 근거로 SLA를 들 수도 없습니다.
참고로 locations 문서에는 BigQuery가 조만간 multi-region이라는 용어 자체를 폐기하고 US·EU를 단일 리전 목록에 편입할 예정이라는 안내가 붙었습니다(US는 us-central1, EU는 europe-west4와 colocate). 이름이 주던 오해가 컸다는 방증이기도 합니다.
1-2. 리전이 다르면 join이 안 된다
쿼리(job)에 관련된 모든 dataset은 job의 location과 일치해야 합니다. 읽는 쪽이든 쓰는 쪽이든 마찬가지입니다. 어긋나면 에러가 납니다.
여기에 함정이 하나 있습니다. US 멀티리전과 us-central1은 지리적으로 겹쳐도 location으로는 불일치입니다. 예전에는 이 조합이 무조건 에러였습니다. 지금은 프로젝트에서 global query(Preview)를 켜 두면 실행됩니다. 기본값은 꺼져 있어서 ALTER PROJECT SET OPTIONS로 활성화하고 전용 권한도 받아야 합니다. 필요한 데이터를 쿼리 실행 location으로 복사해 와서 처리하는 방식이라, 리전 간 전송 때문에 지연이 크고 복사분에 과금도 붙습니다.
reservation(슬롯)도 리전에 묶입니다. EU에 슬롯을 사 두고 US dataset을 쿼리하면 그 쿼리는 reservation을 타지 않고 on-demand로 과금됩니다. 슬롯 커밋이 있는 조직에서 location을 섞어 쓰면 조용히 이중 과금 구조가 됩니다.
location을 명시하지 않으면 요청이 참조하는 리소스에서 결정되고, 그것으로도 안 되면 설정해 둔 기본 location, 그마저 없으면 US 멀티리전입니다. EU 데이터 residency 규제 환경이라면 스크립트성 job이 조용히 US에서 돌 수 있다는 뜻이라, 규제가 걸린 조직은 job location을 항상 명시하는 쪽이 안전합니다.
1-3. Dataset location은 생성 후 변경 불가
dataset의 location은 만들고 나면 바꿀 수 없습니다. 옮기려면 새 location에 dataset을 만들어 다시 적재하거나, cross-region dataset copy를 씁니다. 보통은 후자가 간편한데, 내부적으로 BigQuery Data Transfer Service가 데이터를 옮겨 줍니다.
2. 기본 내구성은 어디까지 막아 주는가
BigQuery는 리전이든 멀티리전이든, 선택된 단일 리전 안의 서로 다른 두 zone에 데이터 사본을 자동 저장합니다. 컴퓨트 용량도 여러 zone에 걸쳐 여분을 유지합니다. 별도 설정 없이 기본으로 제공됩니다.
- Zone 장애 (정전, 네트워크 분리, 물리 손상): soft든 hard든 데이터 손실은 없다고 문서가 명시합니다(no data loss is expected). 실행 중이던 쿼리가 실패해 재제출이 필요할 수는 있습니다
- Soft 리전 장애 (리전 전체 네트워크 단절 등): 리전이 복구될 때까지 가용성을 잃지만 데이터는 잃지 않습니다
- Hard 리전 장애 (물리적 리전 손실): 데이터 손실이 가능하고, BigQuery는 이 경우의 내구성을 보장하지 않습니다. 멀티리전도 마찬가지입니다
즉 기본 제공 안전망은 한 리전 안에서 난 사고까지입니다. 리전 자체가 무너지는 시나리오는 이제부터 나오는 기능들을 직접 조합해서 대비해야 합니다.
3. Time travel과 fail-safe
리전 장애보다 훨씬 자주 일어나는 건 사람이 내는 실수입니다. 잘못된 DELETE, 실수로 날린 테이블, 배포 중 오염된 데이터가 여기 들어갑니다. 시간을 되돌려야 하는 이런 상황을 time travel과 fail-safe가 받습니다.
3-1. 직접 되돌릴 수 있는 최대 7일
time travel은 변경·삭제된 데이터를 일정 기간 보존해서 과거 시점 조회와 복구를 가능하게 합니다. 기본 7일이고, dataset(또는 project) 단위로 2~7일 사이에서 조정할 수 있습니다. 테이블 단위 설정은 안 됩니다.
과거 시점 조회는 FOR SYSTEM_TIME AS OF로 합니다.
-- 1시간 전 상태 조회
SELECT *
FROM `project.dataset.events`
FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR);삭제된 테이블은 이 절로 조회할 수 없습니다. 삭제 전 데이터를 보려면 테이블을 먼저 복원해야 하는데, bq cp에 @ 스냅샷 데코레이터(Unix epoch 밀리초)를 붙입니다.
# 삭제 직전 시점으로 테이블 복원
bq cp my_dataset.my_table@1757700000000 my_dataset.my_table_restored테이블을 지운 뒤 같은 이름으로 새 테이블을 만들었어도, 원본이 존재했던 시점의 타임스탬프를 지정하면 원본 데이터를 복구할 수 있습니다.
dataset을 통째로 지웠다면 UNDROP SCHEMA로 복구합니다. 역시 time travel 창 안에서만 되고, 같은 이름의 dataset을 그 리전에 이미 만들어 뒀으면 ALREADY_EXISTS로 실패합니다. '일단 같은 이름으로 새로 만들고 나중에 복구'가 복구를 막아 버리는 셈이라, 사고 후엔 복구부터 하는 게 순서입니다.
복원에도 한계가 있습니다. 전체 테이블 단위로만 되고 개별 파티션은 복원할 수 없으며, 원본의 태그·파티셔닝 정보는 복사되지 않습니다. 복원 후 파티셔닝·클러스터링을 다시 걸어 주는 후처리까지가 복구 작업입니다.
거대한 테이블에서 특정 파티션 하나만 망가진 경우라면 통째 롤백이 부담스럽습니다. 이럴 땐 FOR SYSTEM_TIME AS OF로 그 파티션 범위만 과거 시점에서 읽어 임시 테이블에 담고, 원본의 해당 파티션에 덮어쓰는 우회를 씁니다.
-- 망가진 하루치 파티션만 2시간 전 상태로 되돌리기
CREATE OR REPLACE TABLE `project.dataset.events_fix` AS
SELECT * FROM `project.dataset.events`
FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 2 HOUR)
WHERE DATE(event_ts) = '2026-09-12';
-- 이후 해당 파티션을 events_fix 내용으로 교체3-2. fail-safe 7일은 셀프서비스가 아니다
time travel 창이 끝난 뒤에도 BigQuery는 삭제 데이터를 추가 7일간 fail-safe 스토리지에 보존합니다. 이 기간은 조정할 수 없습니다.
fail-safe 데이터는 직접 쿼리할 수도, 스스로 복구할 수도 없습니다. Cloud Customer Care에 요청해야만 복구됩니다. 'BigQuery는 14일까지 복구 가능'이라는 통념의 절반은 셀프서비스가 아니라 지원 티켓이라는 뜻입니다. 복구 시점도 사용자가 정하지 못합니다. 실질적인 자력 복구 창은 time travel의 최대 7일입니다.
3-3. physical billing에서만 청구된다
2편에서 다룬 storage billing 모델과 여기서 만납니다. physical billing 모델에서는 time travel과 fail-safe 바이트가 별도로 과금됩니다. logical billing에서는 애초에 청구 대상이 아닙니다.
이 차이가 전략을 가릅니다. time travel 창을 2일로 줄여 스토리지 비용을 아끼는 전략은 physical billing에서만 의미가 있고, 줄여도 fail-safe 7일은 고정이라 그 몫의 과금은 남습니다. DML(UPDATE/DELETE/MERGE)이 잦은 테이블은 time travel 바이트가 계속 쌓입니다. physical billing으로 넘어갈 때 이 몫이 조용히 따라붙습니다.
4. 스냅샷과 클론
time travel은 7일이 한계입니다. '지난달 마감 시점 상태를 보존'처럼 더 긴 보존이 필요하면 테이블 스냅샷을 씁니다.
4-1. 스냅샷은 읽기 전용이다
CREATE SNAPSHOT TABLE `project.backup.events_20260901`
CLONE `project.dataset.events`
OPTIONS (expiration_timestamp = TIMESTAMP '2027-03-01 00:00:00 UTC');스냅샷은 생성 시점의 데이터를 읽기 전용으로 보존합니다. 과금이 독특한데, base 테이블과 공유하는 데이터에는 요금이 붙지 않고 차분만 과금됩니다. 생성 직후엔 스토리지 비용이 0이고, base에서 데이터가 변경·삭제될수록 그 몫이 스냅샷 쪽 과금으로 잡히기 시작합니다. '공짜 백업'처럼 보이지만 base가 활발히 바뀌는 테이블이면 차분이 금방 쌓입니다.
time travel과 조합하면 과거 7일 이내의 임의 시점 기준 스냅샷도 만들 수 있습니다. 다만 7일 밖은 안 되므로, '매월 1일 상태 보존' 같은 요구는 미리 스케줄을 걸어야 합니다. 전용 기능은 없고 scheduled query로 CREATE SNAPSHOT TABLE을 반복 실행하는 방식입니다.
스냅샷은 표준 테이블처럼 바로 쿼리할 수 있습니다. 다만 읽기 전용이라 수정 가능한 테이블로 되살리려면 restore 단계를 거쳐야 합니다.
4-2. 클론은 쓰기가 된다
클론은 스냅샷의 쓰기 가능 버전입니다. 과금 모델은 같습니다(차분만). '운영 테이블을 복사해서 실험'하는 용도라면 restore가 필요한 스냅샷보다 클론이 맞습니다. 뷰·구체화된 뷰·외부 테이블은 클론할 수 없습니다.
4-3. 둘 다 리전을 벗어날 수 없다
스냅샷과 클론은 base 테이블과 같은 리전, 같은 조직 안에만 만들 수 있습니다. 리전 장애 대비 백업으로는 처음부터 쓸 수 없다는 뜻입니다. 실수 삭제나 오염에는 유효하지만, 리전이 통째로 무너지는 쪽에는 별도 수단이 필요합니다.
리전 밖으로 백업을 빼는 고전적인 방법은 GCS 내보내기입니다. extract job(bq extract)은 공유 슬롯 풀 기준 하루 50TiB까지 무료인데, 같은 내보내기라도 EXPORT DATA SQL 문은 쿼리 처리 비용이 과금되니 경로를 구분해야 합니다. 파일당 1GB 제한 때문에 와일드카드 URI가 사실상 필수이고, 테이블 하나당 job 하나라 dataset 전체 백업은 오케스트레이션이 필요합니다.
5. Cross-region replication
5-1. Primary/Secondary 모델
dataset 단위로 다른 리전에 복제본을 만들 수 있습니다.
-- us-central1의 dataset에 us-east4 복제본 추가
ALTER SCHEMA `project.my_dataset`
ADD REPLICA `us-east4`
OPTIONS (location = 'us-east4');primary replica만 쓰기가 되고 secondary는 읽기 전용입니다. primary에 대한 쓰기는 비동기로 secondary에 전파됩니다. secondary 리전에서 쿼리를 돌리면 그 리전의 슬롯(또는 on-demand)이 필요합니다.
각 리전이 앞에서 본 zone 이중화를 그대로 가지므로, 복제를 걸면 물리 사본은 총 4개(리전당 2 zone)가 됩니다.
5-2. 승격은 계획된 스위치오버
secondary를 primary로 승격하는 문은 이렇습니다.
ALTER SCHEMA `project.my_dataset`
SET OPTIONS (primary_replica = 'us-east4');이 문은 job location을 secondary 리전으로 명시해서 실행해야 합니다(bq query --location=us-east4, 콘솔이면 query settings에서 지정). 공식 문서가 요구사항으로 못 박은 부분이고, 지정하지 않으면 primary 리전으로 라우팅됩니다.
승격은 secondary가 primary를 따라잡은 상태라면 몇 초 안에 끝납니다. 따라잡지 못했으면 따라잡을 때까지 완료되지 않고, 그동안 모든 쓰기가 에러를 반환합니다. 복제 지연은 Cloud Monitoring의 replication latency 지표로 관측할 수 있고, 스트리밍(Storage Write API)으로 들어온 데이터의 복제는 best-effort라 지연이 크게 튈 수 있습니다. 스트리밍 중심 파이프라인이면 RPO를 사실상 보장 못 한다는 점을 설계에 반영해야 합니다.
승격 후 챙길 게 하나 더 있습니다. scheduled query는 생성 시점의 리전에 묶여 있어서 승격을 따라가지 않습니다. 구 primary에 쓰기를 하는 예약 쿼리는 승격 후 읽기 전용이 된 그쪽을 건드리다 전부 실패합니다. 읽기만 하는 쿼리는 구 리전이 살아 있으면 돌긴 하는데 오래된 데이터를 읽고, 그 리전이 다운되면 같이 실패합니다. 어느 쪽이든 승격 런북에 scheduled query 재생성이 들어가야 합니다.
5-3. 자주 걸리는 제약
복제를 켜기 전에 확인할 제약이 몇 가지 있습니다.
- CMEK: 키는 자동 복제되지 않습니다.
replica_kms_key를 설정하지 않으면 CMEK 적용 테이블을 secondary에서 조회할 수 없고, 이 키는 replica 생성 후 변경도 안 됩니다 - 미지원 대상: BigLake 테이블, external·federated dataset, BigQuery Omni 로케이션은 복제되지 않습니다
- dataset당 리전별 replica는 1개, 테이블 10만 개 미만인 dataset만 복제 가능
- 같은 리전에 replica를 추가·삭제하는 건 하루 4회까지
- 목적지 리전 크기 한도는 us·eu 멀티리전 10PB, 그 외 리전 500TB가 기본값
앞의 두 개가 특히 조용히 물립니다. 복제를 걸어 두고 "왜 secondary에서 일부 테이블이 안 보이지"로 헤매는 경우가 대개 CMEK 아니면 외부 테이블입니다.
5-4. 비용은 3중
- secondary 리전의 스토리지가 별도 사본으로 과금 (long-term storage 상태는 복제 후에도 유지)
- 복제 데이터 전송료: physical bytes 기준, 북미 안에서 GiB당 $0.02 수준
- secondary에서 쿼리하면 그 리전의 컴퓨트 비용
복제 작업 자체는 BigQuery가 관리해서 사용자 슬롯을 먹지는 않습니다.
6. 복제본은 백업이 아니다
첫째, 삭제도 복제됩니다. primary에서 실행된 DELETE, UPDATE, DROP이 비동기로 그대로 secondary에 전파됩니다. 실수로 테이블을 날리면 복제본에서도 사라집니다. 실수 삭제의 복구는 time travel과 스냅샷의 몫이지, 복제본은 그 용도가 아닙니다.
둘째, primary 리전이 완전히 죽으면 승격이 안 됩니다. 승격 DDL 자체가 동작하려면 시스템이 정상이어야 합니다. 공식 문서도 cross-region replication은 리전 전체 장애 시의 DR 용도가 아니라고 명시합니다. 이 기능의 실제 용도는 리전이 살아 있을 때 하는 계획된 스위치오버입니다. 마이그레이션이거나, 가까운 리전으로 읽기를 분산하는 경우입니다.
리전이 통째로 죽는 시나리오에 실제로 듣는 수단은 따로 있습니다. 단, Enterprise Plus edition이 전제입니다.
7. Managed disaster recovery
7-1. Enterprise Plus 전용, compute까지 failover
managed DR은 Enterprise Plus edition 전용입니다. 핵심 개념은 failover reservation으로, 스토리지(dataset 복제본)와 컴퓨트(슬롯)를 한 단위로 묶어 함께 failover합니다. 스토리지 쪽은 내부적으로 cross-region replication을 쓰되, 일반 복제보다 빠른 Turbo replication으로 돕니다. 단 최초 backfill에는 Turbo가 적용되지 않아서, DR 구성 직후의 초기 동기화는 15분 목표 대상이 아닙니다. 구성이 끝났다고 보호가 시작된 건 아닙니다.
문서가 제시하는 목표치는 두 가지입니다.
- RTO 5분: failover 개시 후 5분 안에 secondary 리전에서 컴퓨트 사용 가능
- RPO 15분: secondary를 primary 기준 15분 이내로 유지하는 것이 목표
RPO 쪽 원문은 'aims to'입니다. 보장(SLA)이 아니라 목표치입니다. 대역폭 쿼터를 넘거나 스트리밍이 몰리면 깨질 수 있습니다.
7-2. Hard failover와 soft failover
- Hard failover: primary가 오프라인이어도 secondary를 즉시 승격. replication_time 이후 커밋된 데이터는 유실됩니다. 데이터만이 아니라 아직 복제 안 된 dataset ACL(authorized view, 사용자 권한)도 날아갈 수 있어서, 공식 문서가 failover 후 Terraform apply 같은 권한 재적용을 권합니다
- Soft failover: primary의 변경이 모두 복제될 때까지 기다렸다가 전환. 데이터 손실이 없지만 양쪽 리전이 다 살아 있어야 완료됩니다. 전환을 걸어 둔 상태에서 primary가 죽으면 남은 델타를 넘겨받을 방법이 없어 그대로 대기에 빠집니다. secondary 쪽 슬롯 쿼터가 모자라도 트리거 자체가 안 되고, 진행 중에는 붙어 있는 dataset이 읽기 전용이 됩니다
hard와 soft는 용도가 갈립니다. 진짜 리전 전면 장애에서 쓸 수 있는 건 hard failover(손실 감수)뿐이고, soft failover의 실제 용도는 DR 훈련과 계획된 이전입니다. 참고로 soft failover는 2025년 9월에야 GA된 후발 기능이라, 그 전에 쓰인 자료에는 hard만 나옵니다.
운영 디테일 몇 가지를 덧붙이면, failover reservation에 dataset을 붙이는 순간 Enterprise Plus 예약만 그 dataset에 쓰기가 가능해지고(on-demand·다른 edition 쓰기 차단), 컴퓨트 비용은 primary 쪽만 내면 됩니다(secondary 대기 용량은 reservation baseline 기준 무료).
managed DR 대상 dataset에서는 load·extract job이 무료 공유 슬롯 풀을 쓸 수 없습니다. 앞에서 extract가 하루 50TiB까지 무료라고 했는데, DR을 켠 dataset에는 그 경로가 막힙니다. GCS 백업을 병행할 계획이라면 이 몫을 비용에 넣어야 합니다.
8. 언제 무엇을 쓰는가
표 위쪽 사고가 압도적으로 자주 일어나고, 위쪽 층일수록 싸고 기본 제공에 가깝습니다. 아래로 갈수록 드물고 비쌉니다. 리전 장애 대비를 검토하기 전에 time travel 창 설정과 스냅샷 스케줄부터 정비하는 게 순서입니다. 큰돈 들여 managed DR을 깔아 놓고 실수 삭제에는 무방비인 조직이 생각보다 많습니다.
마무리
리전 설계는 결국 어떤 사고까지 감당할 것인지를 정하고 거기 맞는 수단만 사는 문제로 수렴합니다. 이번 편에서 다룬 내용도 앞 편과 이어져 있습니다. 2편의 storage billing 모델이 time travel 과금으로 돌아왔고, 7편의 Storage Write API는 복제 지연 문제로 다시 나왔습니다.
열 편에서 걸린 자리도 대개 같은 종류였습니다. 4편의 EXPLAIN, 5편의 파티션 한도, 6편의 authorized MV, 7편의 buffered stream, 9편의 bigquery.user가 전부 대충 알고 있던 것과 문서에 실제로 적힌 것이 어긋난 지점이었고, 그 차이가 장애 당일의 몇 시간을 가릅니다.
이 시리즈는 BigQuery 문서를 읽다 걸린 자리를 모아 둔 기록입니다. 문서와 실제 동작이 어긋나는 지점은 앞으로도 나올 테니, 걸릴 때마다 이 기록도 갱신하겠습니다.