Google PCD: 애플리케이션 데이터, 상태 및 스토리지 패턴 — 학습 가이드
다음의 일부입니다: Google Professional Cloud Developer — 학습 가이드. 검증된 답안으로 연습하기: Google 시험 허브, 또는 다음에서 시간 제한 모의고사 풀기: ExamRoll.io.
개요
Google Cloud의 최신 애플리케이션은 지연 시간, 일관성, 확장성, 비용, 운영 복잡성의 균형을 맞추기 위해 여러 데이터 저장소를 조합하여 사용하는 것이 일반적입니다. 목적에 맞는 서비스와 패턴을 선택하고 그 장애 모드를 이해하는 것은 탄력적인 설계의 핵심입니다. 이 섹션에서는 Cloud SQL, Cloud Spanner, Firestore, Bigtable, Memorystore, Cloud Storage에 대한 실용적인 가이드라인을 요약하고, 마이그레이션, 파티셔닝, 데이터 보호에 대해 다룹니다.
Cloud SQL의 관계형 데이터
Cloud SQL은 친숙한 RDBMS 시맨틱을 갖춘 관리형 MySQL, PostgreSQL, SQL Server를 제공합니다.
비공개 연결
- 비공개 IP를 사용하여 데이터베이스 트래픽을 VPC 내에서 유지하세요. 이를 통해 공개 인그레스 규칙과 IP 허용 목록이 필요 없어지고 NAT 이그레스의 복잡성을 피할 수 있습니다.
- 경로와 방화벽 규칙이 VPC에서 인스턴스로의 트래픽을 허용하는지 확인하세요. 비공개 IP를 활성화하면 비공개 IP에 대한 이름 확인이 자동으로 처리됩니다.
- 서버리스(Cloud Run, App Engine, Cloud Functions)의 경우, 비공개 IP를 사용하더라도 IAM 인증 및 TLS를 처리하는 Cloud SQL 커넥터를 사용하는 것이 좋습니다.
고가용성 및 복제본
- 리전 HA는 기본 인스턴스와 대기 인스턴스를 서로 다른 영역에 배치하고 동기식 디스크 복제를 사용합니다. 장애 조치 시 일시적인 연결 끊김이 예상되므로, 앱은 일시적인 오류를 재시도하고 다시 연결해야 합니다.
- 읽기 복제본은 비동기식이며 읽기 트래픽을 분산시킵니다. DR 및 읽기 근접성을 위해 리전 간 복제본을 사용하되, 복제본은 최종적 일관성을 갖는다는 점을 이해해야 합니다.
- 복구 또는 계획된 역할 전환을 위해 읽기 복제본을 승격시키세요. 승격 절차를 정기적으로 테스트하세요.
백업 및 특정 시점 복구(PITR)
- 자동 백업과 트랜잭션/PITR 로그를 활성화하세요. IO 경합을 줄이기 위해 사용량이 적은 시간대에 백업을 예약하세요.
- 여러 복사본을 보관하고 별도의 인스턴스로 주기적으로 복원을 검증하세요. 복원할 수 없는 백업은 운영상 백업이 없는 것과 같습니다.
커넥션 풀 및 한도
- Cloud SQL은 최대 연결 수를 강제합니다. 수명이 짧은 연결이 과도하게 많으면 CPU 스래싱과 지연 시간이 발생합니다. 애플리케이션 측 풀링(예: HikariCP, PgBouncer, ProxySQL)을 사용하세요.
- 인스턴스 메모리만으로 풀 크기를 정하지 말고, CPU 코어와 워크로드 동시성을 기반으로 크기를 정하세요. 작게 시작하여 경험적으로 확장하세요.
- 임시/서버리스 컴퓨팅의 경우, 언어별 Cloud SQL 커넥터가 버전별 풀을 유지하지만, 콜드 스타트 후 커넥션 폭풍을 피하기 위해 동시성을 제한해야 합니다.
데이터 파티셔닝 및 성능
- 경합을 줄이기 위해 대규모 멀티테넌트 스키마를 고객 또는 리전별로 샤딩하세요. 가능한 경우 핫 테넌트를 격리된 상태로 유지하세요.
- 커버링 인덱스를 신중하게 생성하세요. 과도한 인덱싱은 쓰기 속도를 저하시키고 스토리지 사용량을 증가시킵니다. 카디널리티와 조건자 선택도를 확인하세요.
- 핫 로우에 대해서는 낙관적 잠금 또는 SELECT FOR UPDATE를 사용하세요. 지속적인 쓰기 워크로드의 경우 autovacuum(PostgreSQL) 또는 InnoDB 설정(MySQL)을 조정하세요.
일반적인 장애 모드 및 완화 조치:
- VM/노드 재시작 후 Thundering herd(동시 요청 쇄도): 풀 크기를 제한하고 지수 백오프를 사용합니다.
- 읽기-쓰기 일관성을 위한 복제본 지연: 세션 일관성이 필요한 경우 읽기 작업을 기본 인스턴스에 고정합니다.
- 노이지 네이버(리소스 경합) 또는 유지보수로 인한 HA 장애 조치 플래핑(불안정): 멱등성을 갖춘 커넥션 및 트랜잭션 재시도를 구현합니다.
전 세계 규모의 관계형 데이터베이스, Cloud Spanner
Cloud Spanner는 전역적 일관성 옵션을 갖춘 수평적 확장성을 제공합니다.
일관성 및 트랜잭션
- 강력한 읽기 및 읽기-쓰기 트랜잭션은 TrueTime을 사용하여 엄격한 외부 일관성을 제공합니다. 커밋은 선형성을 보장하기 위해 잠시 대기합니다.
- 오래된 읽기 및 제한된 시점의 읽기는 데이터 최신성 요구사항이 약간 완화될 수 있는 읽기 중심 워크로드에서 지연 시간을 줄이고 가용성을 향상시킵니다.
- 읽기 전용 트랜잭션은 잠금 없이 특정 타임스탬프에서 여러 읽기를 수행합니다. 일관된 분석 스냅샷에 사용하세요.
리전, 가용성 및 지연 시간
- 리전 인스턴스는 한 리전 내에서 고가용성을 제공합니다. 다중 리전 구성(예: nam-asia-eur1)은 전 세계적으로 일관된 쓰기를 지원하면서 대륙 간에 매우 높은 가용성과 지연 시간이 짧은 로컬 읽기를 제공합니다.
- 사용자 지리에 맞는 인스턴스 구성을 선택하세요. 쓰기 지연 시간은 대륙 간 쿼럼 크기에 따라 증가합니다.
확장성 및 스키마 설계
- Spanner는 기본 키 범위를 기준으로 데이터를 스플릿으로 샤딩하여 노드에 분산시킵니다. 키가 단조롭게 증가할 때 핫스팟이 발생합니다. 자동 증가 ID나 항상 증가하는 타임스탬프 같은 키를 선행 위치에 사용하는 것을 피하세요.
- 쓰기를 분산시키는 복합 기본 키(예: customer_hash, customer_id, reverse_timestamp)를 사용하세요.
- 인터리브된 테이블은 자식 행을 부모 행과 함께 배치하여 지역성을 높이고 효율적인 조인을 가능하게 합니다. 자식 카디널리티와 액세스가 부모와 강하게 연관될 때 사용하세요. 보조 인덱스로 보완하고, 테이블 조회를 줄이기 위해 STORING 절 사용을 고려하세요.
- CPU, 스토리지, 높은 우선순위 작업과 최선형 작업을 모니터링하고, P95 지연 시간 미만으로 헤드룸을 유지하도록 노드를 확장하세요.
운영 패턴
- 클라이언트는 세션 풀을 사용합니다. 생성 폭풍을 피하기 위해 최소/최대 세션 수를 조정하세요. 재시도는 제한적이고 멱등성을 가져야 합니다. ABORTED 발생 시, 백오프를 사용하여 읽기-쓰기 트랜잭션을 재시도하세요.
- 백업은 가볍고 일관성이 있습니다. 별도의 인스턴스로 복원을 검증하세요. 변경 스트림 및 CDC 통합은 다운스트림 시스템을 구동할 수 있습니다.
절충점:
- 강력한 전역 쓰기는 커밋 대기를 추가합니다. UX에 중요하고 대부분 읽기 작업인 경로에는 오래된 읽기를 사용하세요.
- 인터리빙은 지역성을 향상시키지만 쓰기 부하를 집중시킬 수 있습니다. 운영 환경과 유사한 트래픽으로 테스트하세요.
NoSQL 운영 스토어: Firestore 및 Bigtable
쿼리 패턴과 처리량 프로필에 맞는 NoSQL 모델을 선택하세요.
Firestore (문서)
- 데이터 모델: 컬렉션은 문서를 포함하고, 문서는 하위 컬렉션을 가질 수 있습니다. 쿼리 패턴을 중심으로 모델링하고, 단일 “핫” 문서에 대한 깊은 팬아웃(fan-out) 쓰기는 피하세요.
- 액세스 및 트랜잭션: Native 모드에서 문서 읽기 및 쿼리는 강력한 일관성을 가집니다. 여러 문서에 걸쳐 최대 한 번(at-most-once)의 원자성을 보장하려면 일괄 쓰기(batched writes)를 사용하고, 경합 확인(contention checks)이 포함된 읽기-수정-쓰기(read-modify-write) 작업에는 트랜잭션을 사용하세요.
- 색인: 단일 필드 색인은 자동으로 생성됩니다. 여러 범위/부등식 필터나 정렬 순서를 사용할 때는 다중 필드 복합 색인을 정의해야 합니다. 쿼리를 색인 전용(index-only)으로 만들기 위해 비정규화(Denormalization)가 흔히 사용됩니다.
- 클라이언트 동기화: 실시간 리스너(real-time listeners)가 변경 사항을 스트리밍합니다. 오프라인 캐시는 마지막 쓰기 우선(last-write-wins) 의미 체계에 따라 조정됩니다. 무한한 리스너 팬아웃을 방지하고, 쿼리 커서와 필터를 사용하는 것이 좋습니다.
- 제한 및 장애 모드: 단일 문서에 대한 쓰기 속도는 직렬화됩니다. 하나의 문서에 대한 지속적인 높은 QPS 업데이트는 경합을 유발합니다. N개의 하위 문서를 사용하는 샤딩된 카운터(sharded counters)를 사용하고 읽기 시에 집계하세요.
Cloud Bigtable (와이드 컬럼)
- 행 키(Row-key) 디자인이 가장 중요합니다. Bigtable은 행을 사전순으로 분할하며, 선행 키 세그먼트가 핫스팟(hotspotting) 발생 여부를 결정합니다. 타임스탬프를 앞세우거나 샤딩되지 않은 사용자 ID와 같은 순차적인 키는 피하세요.
- 패턴:
- 시계열 데이터 읽기를 위해 키 내에 타임스탬프를 역순으로 배치: key = device#hash(device_id)#reverse_ts.
- 쓰기 작업을 분산시키기 위해 첫 번째 구성 요소를 해시 또는 버킷으로 나눔: bucket = crc32(user_id) % 128.
- 작고 많은 열로 구성된 셀을 저장하고, 여러 태블릿(tablet)에 걸쳐 있는 큰 행은 피하세요. 액세스 제어 및 GC 정책 분리를 위해 여러 column family를 활용하세요.
- 처리량 및 서빙:
- 복제 및 리전 내 읽기 근접성을 위해 여러 클러스터를 사용하세요. 클러스터 간 쓰기는 최종적 일관성(eventually consistent)을 갖게 됩니다.
- 앱 프로필과 라우팅을 조정하고, 클라이언트 측 스레드 풀과 채널 풀을 충분히 유지하세요.
- GC 및 TTL: 버전 및 시간 기반 GC는 오래된 셀을 비동기적으로 제거합니다. 데이터는 압축(compaction)될 때까지 유지되므로, 규제 기한 준수를 위해 즉각적인 삭제에 의존해서는 안 됩니다.
캐싱 및 객체 스토리지 패턴
Memorystore (Redis/Memcached)
- 캐싱 전략:
- Read-through(읽기 관통): 애플리케이션이 캐시에서 데이터를 가져옵니다. 캐시에 데이터가 없으면(miss), 원본에서 로드하여 캐시를 채웁니다.
- Write-through(쓰기 관통): 쓰기 작업이 캐시와 원본에 동기적으로 이루어집니다.
- Write-behind(후방 쓰기): 쓰기 작업을 캐시에 버퍼링하고 비동기적으로 플러시합니다. 데이터 손실 위험이 있으므로 주의해서 사용해야 합니다.
- 만료 및 무효화:
- 데이터의 부실(staleness) 허용 범위에 맞춰 TTL을 적용하세요. 원본 데이터(source-of-truth)가 변경되면 키를 무효화합니다. 집계 캐시의 경우, 스탬피드(stampede) 현상을 피하기 위해 버전이 지정된 키를 사용하세요.
- 인기 있는 키에 대한 캐시 스탬피드(cache stampede)를 방지하기 위해 뮤텍스(mutex)나 single-flight 패턴을 사용하세요.
- 세션: 임시 세션 데이터를 TTL과 함께 저장합니다. 민감한 데이터인 경우 값을 암호화하거나 불투명 토큰(opaque token)만 저장하세요.
- Redis를 사용한 요청 비율 제한(Rate limiting):
- 고정 윈도우(Fixed-window): ID별 키에 대해 INCR과 EXPIRE를 함께 사용합니다.
- 더 유연한 제한을 위해 슬라이딩 윈도우(Sliding-window)나 토큰 버킷(token-bucket)을 사용합니다. 원자성을 보장하기 위해 Lua 스크립트 사용을 고려하세요.
- 가용성: 기본(Basic) 등급은 장애 조치(failover) 기능이 없습니다. 표준(Standard) 등급은 리전 내 고가용성(HA)을 제공합니다. 캐시는 휘발성으로 취급하고, 절대 신뢰할 수 있는 스토리지로 사용해서는 안 됩니다.
예시: 간단한 고정 윈도우 요청 비율 제한
- 명령어:
- INCR rate:login:USER123:20260903T1000
- EXPIRE rate:login:USER123:20260903T1000 60
- 캐싱 전략:
Cloud Storage
- 객체 및 일관성: 읽기, 쓰기, 덮어쓰기, 삭제, 목록 조회에 대해 강력한 전역 일관성(strong global consistency)을 제공합니다. 객체는 변경 불가능(immutable)하며, 업데이트 시 새로운 버전(generation)이 생성됩니다.
- 서명된 URL(Signed URLs): 앱을 프록시로 거치지 않고 클라이언트와 버킷 간에 직접 대용량 업로드/다운로드를 오프로드합니다. 만료 기간을 짧게 설정하고, 메서드, 경로, 콘텐츠 헤더를 제한하세요.
- 재개 가능한 업로드(Resumable uploads): 5MB보다 큰 파일이나 불안정한 네트워크 환경에서 사용합니다. 5xx/429 오류는 잘린 지수 백오프(truncated exponential backoff)와 재개 토큰(resume token)으로 처리하세요.
- 수명 주기(Lifecycle): 스토리지 클래스를 전환하고, 이전 버전을 삭제하며, 보관 정책을 적용하는 규칙을 정의합니다. 배포 중 안전을 위해 객체 버전 관리(object versioning)와 결합하여 사용하세요.
- 알림: Pub/Sub 알림을 통합하여 객체 생성 완료(finalize)/삭제 시 다운스트림 처리를 트리거하고, 경쟁 조건(race)을 방지하기 위해 전제 조건(precondition, 예: ifGenerationMatch)을 포함하세요.
예시: 로컬 파일 업로드
- gsutil cp ./data/*.parquet gs://my-bucket/ingest/
이 문제 연습하기 → · ExamRoll.io에서 시간 제한 연습 →
Pass the whole exam — not just this question
You found this answer. Get every verified question and explanation in one place, and save hours of prep. Free to start.
시험 합격하기 →