13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] 핫 파티션

  • [Cloud] Cosmos DB 마이그레이션 가이드: 기존 DB에서 NoSQL로 안전하게 전환하기

    [Cloud] Cosmos DB 마이그레이션 가이드: 기존 DB에서 NoSQL로 안전하게 전환하기

    안녕하세요, 13년차 인프라 엔지니어입니다. 오늘은 저처럼 관계형 데이터베이스(Relational Database) 환경에 익숙한 분들이 NoSQL로 전환할 때 겪는 Cosmos DB 마이그레이션 경험담을 풀어볼까 합니다. 특히 기존 DB에서 NoSQL로 넘어갈 때 마주치는 문제들과 제가 삽질 끝에 찾아낸 해결 방안들을 여과 없이 공유하려고 해요.

    최근 서비스가 빠르게 성장하면서 기존 RDB로는 감당하기 어려운 트래픽과 데이터 규모에 직면하는 경우가 많아졌습니다. 저도 직접 운영하는 서비스에서 비슷한 고민을 했거든요. 이럴 때 확장성과 유연성이 뛰어난 NoSQL 데이터베이스, 특히 Azure Cosmos DB 같은 글로벌 분산 데이터베이스가 매력적인 대안으로 떠오릅니다. 하지만 익숙한 환경을 떠나 새로운 패러다임으로 넘어가는 건 결코 쉽지 않죠. 😅

    기존 관계형 데이터베이스에서 Azure Cosmos DB로 마이그레이션하는 전체 아키텍처 개요도

    Cosmos DB와 NoSQL 마이그레이션, 왜 필요한가?

    Cosmos DB는 Microsoft Azure에서 제공하는 완전 관리형(fully managed) NoSQL 데이터베이스 서비스입니다. 전 세계 어디서든 낮은 지연 시간(low-latency)으로 데이터를 읽고 쓸 수 있으며, 필요에 따라 자동으로 확장(auto-scale)되는 것이 가장 큰 장점이죠. 특히 여러 NoSQL API를 지원해서(SQL API, MongoDB API, Cassandra API, Gremlin API 등) 기존에 사용하던 NoSQL 시스템과 호환성을 유지하면서 Cosmos DB 마이그레이션을 진행할 수 있다는 점이 매력적입니다.

    그렇다면 왜 굳이 NoSQL 마이그레이션을 해야 할까요? 제가 직접 경험해보니 몇 가지 확실한 이유가 있더라고요.

    • 확장성 (Scalability): 폭발적으로 증가하는 데이터와 트래픽을 RDB의 수직 확장(vertical scaling)만으로는 감당하기 어렵습니다. NoSQL은 수평 확장(horizontal scaling)에 유리하죠.
    • 유연한 스키마 (Flexible Schema): RDB는 엄격한 스키마를 요구해서 데이터 모델 변경 시 복잡한 마이그레이션 작업이 필요합니다. NoSQL은 스키마가 유연해서 빠르게 변화하는 비즈니스 요구사항에 대응하기 좋습니다.
    • 글로벌 분산 (Global Distribution): 전 세계 사용자에게 서비스를 제공해야 할 때, Cosmos DB는 몇 번의 클릭만으로 전 세계 리전에 데이터를 복제하고 동기화할 수 있습니다.

    이런 장점들 때문에 데이터베이스 전환을 고민하게 되지만, 막상 시작하면 만만치 않은 벽에 부딪힙니다. 가장 큰 벽은 바로 데이터 모델링과 마이그레이션 전략 수립이더군요.

    마이그레이션 준비: 데이터 모델링이 반이다

    관계형 데이터베이스는 정규화(Normalization)를 통해 데이터 중복을 최소화하고 데이터 무결성을 유지합니다. 하지만 NoSQL은 읽기 성능(read performance)을 최적화하기 위해 비정규화(Denormalization)를 적극적으로 활용하는 경우가 많습니다. 이게 처음엔 정말 낯설더라고요.

    Cosmos DB에서는 파티션 키 (Partition Key) 선정이 정말 중요합니다. 파티션 키는 데이터를 논리적 파티션으로 나누는 기준이 되는데, 이 키를 어떻게 정하느냐에 따라 성능과 비용이 천차만별로 달라지거든요. 잘못 정하면 ‘핫 파티션(Hot Partition)’이 생겨서 특정 파티션에만 부하가 몰려 성능 저하를 겪게 됩니다. 제가 이 부분에서 정말 크게 삽질했어요. ㅎㅎ

    💡 팁: 파티션 키는 카디널리티(Cardinality, 고유한 값의 수)가 높고, 읽기/쓰기 작업이 고르게 분산될 수 있는 필드를 선택하는 것이 좋습니다. 예를 들어, 사용자 ID나 주문 ID 같은 필드가 좋은 후보가 될 수 있죠.

    실전 마이그레이션: 데이터 이동 전략

    이제 실제로 데이터를 옮겨볼 차례입니다. Cosmos DB로 데이터를 마이그레이션하는 방법은 여러 가지가 있습니다.

    1. Azure Cosmos DB Data Migration Tool: GUI 기반의 도구로, CSV, JSON 파일, MongoDB, SQL Server 등 다양한 소스에서 데이터를 가져올 수 있습니다. 간단한 초기 마이그레이션에 유용합니다.
    2. Azure Data Factory (ADF): 대규모 데이터 마이그레이션, ETL(Extract, Transform, Load) 파이프라인 구축에 적합합니다. 다양한 데이터 소스와 싱크를 지원하며, 데이터 변환 로직을 추가할 수 있습니다.
    3. 커스텀 스크립트: Python, .NET 등 SDK를 이용해 직접 스크립트를 작성하여 데이터를 마이그레이션하는 방식입니다. 복잡한 변환 로직이나 실시간 동기화가 필요할 때 유용하죠.

    저는 초기 마이그레이션에는 Data Migration Tool을 사용했지만, 복잡한 데이터 변환이 필요해서 결국 Python 스크립트를 직접 작성했습니다. 기존 관계형 DB에서 데이터를 읽어와 NoSQL 모델에 맞게 변환한 뒤 Cosmos DB에 삽입하는 방식이었거든요.

    먼저 Azure CLI를 이용해 Cosmos DB 계정과 데이터베이스, 컨테이너를 생성하는 기본적인 명령어부터 살펴보겠습니다.

    # Azure 리소스 그룹 생성 (이미 있다면 생략)
    az group create --name myCosmosResourceGroup --location koreacentral
    
    # Cosmos DB 계정 생성 (API 종류는 필요에 따라 선택, 여기서는 SQL API)
    az cosmosdb create \
      --name mycosmosdbaccount13year \
      --resource-group myCosmosResourceGroup \
      --default-consistency-level Session \
      --locations regionName=koreacentral failoverPriority=0 \
      --kind GlobalDocumentDB
    
    # 데이터베이스 생성
    az cosmosdb sql database create \
      --account-name mycosmosdbaccount13year \
      --name myNoSQLDatabase \
      --resource-group myCosmosResourceGroup
    
    # 컨테이너 생성 (파티션 키는 /userId 로 예시)
    az cosmosdb sql container create \
      --account-name mycosmosdbaccount13year \
      --database-name myNoSQLDatabase \
      --name myItemsContainer \
      --partition-key-path /userId \
      --throughput 400 \
      --resource-group myCosmosResourceGroup
    

    그리고 Python으로 데이터를 읽어와 삽입하는 예시 코드입니다. 실제 환경에서는 데이터 변환 로직이 훨씬 복잡하겠죠.

    import os
    import json
    from azure.cosmos import CosmosClient
    
    # Cosmos DB 설정
    COSMOS_DB_ENDPOINT = os.environ.get("COSMOS_DB_ENDPOINT")
    COSMOS_DB_KEY = os.environ.get("COSMOS_DB_KEY")
    DATABASE_NAME = "myNoSQLDatabase"
    CONTAINER_NAME = "myItemsContainer"
    
    # Cosmos DB 클라이언트 초기화
    client = CosmosClient(COSMOS_DB_ENDPOINT, COSMOS_DB_KEY)
    database = client.get_database_client(DATABASE_NAME)
    container = database.get_container_client(CONTAINER_NAME)
    
    def migrate_data(data_list):
        print(f"Migrating {len(data_list)} items...")
        for item in data_list:
            try:
                # 기존 RDB 데이터를 NoSQL 모델에 맞게 변환 (예시)
                # item_transformed = {"id": str(item["old_id"]), "userId": item["user_id"], "productName": item["name"], ...}
                # 실제 데이터 변환 로직은 여기에 구현
                item_transformed = item # 단순 복사 (예시)
                item_transformed["id"] = str(item_transformed["id"]) # Cosmos DB item id는 문자열이어야 함
    
                container.upsert_item(body=item_transformed)
                # print(f"Inserted/Updated item: {item_transformed['id']}")
            except Exception as e:
                print(f"Error processing item {item.get('id', 'N/A')}: {e}")
    
    if __name__ == "__main__":
        # 예시 데이터 (실제로는 RDB에서 조회)
        sample_data = [
            {"id": 1, "userId": "userA", "productName": "Laptop", "price": 1200},
            {"id": 2, "userId": "userB", "productName": "Mouse", "price": 50},
            {"id": 3, "userId": "userA", "productName": "Keyboard", "price": 100},
            {"id": 4, "userId": "userC", "productName": "Monitor", "price": 300},
        ]
        migrate_data(sample_data)
        print("Migration complete!")
    
    Python 스크립트를 활용한 데이터 마이그레이션 과정 개념도

    Python 스크립트를 활용한 데이터 마이그레이션 과정 개념도

    삽질 경험: 파티션 키 잘못 설계했다가 고생했던 이야기 ⚠️

    제가 가장 크게 삽질했던 부분이 바로 파티션 키 (Partition Key) 설계였습니다. 처음에는 무심코 productId를 파티션 키로 잡았거든요. 저희 서비스는 특정 인기 상품에 대한 조회수가 압도적으로 높았는데, 아니나 다를까 얼마 지나지 않아 productId가 높은 특정 파티션에만 요청이 몰려서 Latency(지연 시간)가 급증하고 Request Unit(RU) 소모가 비정상적으로 치솟는 문제가 발생했습니다. 이게 바로 핫 파티션 (Hot Partition) 문제였죠. 😱

    Cosmos DB는 RU라는 개념으로 처리량을 측정하고 비용을 부과하는데, 핫 파티션이 발생하면 특정 파티션의 RU가 한계치에 도달하여 요청이 제한(throttling)될 수 있습니다. 전체 컨테이너의 RU가 여유 있어도 특정 파티션만 과부하가 걸리는 상황이 발생하더라고요. 이때 정말 당황했었습니다.

    해결책은 파티션 키를 userId와 같이 고르게 분산될 수 있는 값으로 변경하는 것이었습니다. 기존 데이터를 다시 마이그레이션해야 하는 대규모 작업이었지만, 장기적인 안정성을 위해서는 필수적인 선택이었죠. 이 경험을 통해 파티션 키 설계가 얼마나 중요한지 뼈저리게 느꼈습니다.

    RU (Request Unit)는 Cosmos DB에서 모든 데이터베이스 작업(읽기, 쓰기, 쿼리 등)에 대한 성능 단위입니다. 1 RU는 1KB 크기의 항목을 읽는 비용과 같습니다. 복잡한 쿼리나 큰 항목을 쓰면 더 많은 RU가 소모되죠. RU 사용량을 모니터링하면서 핫 파티션이 있는지 주기적으로 확인하는 것이 중요합니다.

    마이그레이션 후 검증 및 최적화

    데이터 마이그레이션이 끝났다고 끝이 아닙니다. 오히려 이때부터가 진짜 시작이죠! 마이그레이션 후에는 반드시 다음 사항들을 꼼꼼하게 검증하고 최적화해야 합니다.

    1. 데이터 일관성 (Data Consistency): 원본 데이터와 Cosmos DB의 데이터가 일치하는지 확인해야 합니다. 간단한 스크립트를 통해 레코드 수, 일부 필드의 합계 등을 비교하는 방법이 효과적입니다.
    2. 성능 벤치마킹 (Performance Benchmarking): 예상했던 성능이 나오는지 부하 테스트를 진행해야 합니다. 특히 핵심 비즈니스 로직에 대한 읽기/쓰기 Latency와 RU 소모량을 면밀히 관찰해야 합니다.
    3. 비용 모니터링 (Cost Monitoring): Cosmos DB는 사용량 기반 과금(pay-as-you-go)이므로, 예상치 못한 RU 소모로 비용 폭탄을 맞지 않도록 모니터링 대시보드를 설정하는 것이 중요합니다. Azure Portal에서 RU 사용량을 실시간으로 확인할 수 있습니다.

    Azure Portal에서 Cosmos DB 계정의 ‘메트릭 (Metrics)’ 섹션을 보면 RU 사용량, 지연 시간, 요청 수 등을 그래프로 볼 수 있습니다. 여기서 특정 파티션 키에 대한 요청이 급증하는 패턴이 보인다면 핫 파티션을 의심해봐야 합니다.

    Azure Portal의 Cosmos DB RU 사용량 및 성능 메트릭 대시보드 예시

    Azure Portal의 Cosmos DB RU 사용량 및 성능 메트릭 대시보드 예시

    Cosmos DB 마이그레이션 전략 선택 가이드

    다양한 상황에 따라 마이그레이션 전략도 달라질 수 있습니다. 제가 경험했던 것들을 바탕으로 몇 가지 시나리오별 추천 전략을 정리해봤습니다.

    시나리오 고려 사항 추천 마이그레이션 전략 주의사항
    소규모 초기 마이그레이션
    (데이터 양 < 100GB)
    빠른 전환, 데이터 모델 단순 Azure Cosmos DB Data Migration Tool 대규모 데이터에는 부적합, 복잡한 변환 어려움
    대규모 데이터 마이그레이션
    (데이터 양 > 100GB, 복잡한 ETL 필요)
    데이터 변환, 증분 동기화 Azure Data Factory (ADF) 활용 ADF 파이프라인 설계 및 운영 복잡성 증가
    실시간/Near Real-time 동기화
    (서비스 중단 최소화)
    무중단 전환, 이중 쓰기(Dual-write) 전략 커스텀 스크립트 + Change Feed 활용 이중 쓰기 로직 구현 및 동기화 일관성 관리 복잡
    기존 NoSQL (예: MongoDB) 에서 전환 API 호환성, 스키마 유사성 MongoDB API for Cosmos DB + Mongo Shell 또는 Data Migration Tool 일부 기능/성능 차이 고려
    Cosmos DB 마이그레이션 전략 선택 가이드 인포그래픽

    Cosmos DB 마이그레이션 전략 선택 가이드 인포그래픽

    마무리: 새로운 패러다임으로의 전환

    Cosmos DB 마이그레이션은 단순한 데이터 이동을 넘어, 데이터베이스 패러다임을 전환하는 일입니다. 제가 직접 겪어보니, 기존 관계형 DB의 사고방식에서 벗어나 NoSQL의 특성과 장점을 이해하는 것이 가장 중요하더라고요. 특히 파티션 키 설계나 RU 최적화 같은 부분은 충분한 학습과 테스트 없이는 큰 시행착오를 겪을 수밖에 없습니다.

    하지만 제대로만 설계하고 구현한다면, Cosmos DB는 여러분의 서비스에 강력한 확장성과 유연성을 제공할 것입니다. 이 글이 기존 DB에서 NoSQL로의 전환을 고민하는 분들께 작은 도움이 되었으면 좋겠네요. 다음에 기회가 된다면 Cosmos DB의 Change Feed를 활용한 실시간 데이터 처리 아키텍처에 대해서도 한번 다뤄보겠습니다. 궁금한 점이 있다면 언제든 댓글로 남겨주세요! 😊