Table storage에 대한 성능 및 확장성 검사 목록

Microsoft는 Table Storage를 사용하여 고성능 애플리케이션을 개발하기 위해 검증된 사례를 다양하게 개발했습니다. 이 검사 목록에서는 개발자가 성능을 최적화하기 위해 수행할 수 있는 주요 사례를 식별합니다. 애플리케이션을 설계하는 동안 프로세스 전체에서 이러한 사례를 명심하세요.

Azure Storage에는 용량, 트랜잭션 속도 및 대역폭에 대한 확장성 및 성능 목표가 있습니다. Azure Storage 확장성 목표에 대한 자세한 내용은 표준 스토리지 계정의 확장성 및 성능 목표Table Storage의 확장성 및 성능 목표를 참조하세요.

검사 목록

이 문서에서는 성능에 대해 검증된 사례를 Table storage 애플리케이션 개발 중에 수행할 수 있는 검사 목록으로 구성합니다.

완료 범주 디자인 고려 사항
  확장성 목표 애플리케이션에서 최대 스토리지 계정 수를 초과하여 사용하지 않도록 디자인할 수 있습니까?
  확장성 목표 용량 및 트랜잭션 제한에 도달하지 않도록 방지하고 있습니까?
  확장성 목표 초당 엔터티의 확장 목표에 도달하고 있습니까?
  네트워킹 클라이언트 쪽 디바이스에 필요한 성능을 달성할 수 있을 만큼 충분한 높은 대역폭과 짧은 대기 시간이 있습니까?
  네트워킹 클라이언트 쪽 디바이스에 고품질 네트워크 링크가 있습니까?
  네트워킹 클라이언트 애플리케이션이 스토리지 계정과 동일한 지역에 있습니까?
  직접 클라이언트 액세스 SAS(공유 액세스 서명)와 CORS(원본 간 리소스 공유)를 사용하여 Azure Storage에 직접 액세스할 수 있습니까?
  일괄 처리 애플리케이션에서 엔터티 그룹 트랜잭션을 사용하여 업데이트를 일괄 처리하고 있습니까?
  .NET 구성 .NET Framework 애플리케이션의 경우 클라이언트가 충분한 수의 동시 연결을 사용하도록 구성했습니까?
  .NET 구성 .NET Framework 애플리케이션의 경우 .NET에서 충분한 수의 스레드를 사용하도록 구성했습니까?
  병렬 처리 고객의 기능이 오버로드되지 않거나 확장성 목표에 도달하지 않도록 병렬 처리가 적절히 제한되도록 했습니까?
  도구 Microsoft에서 제공하는 클라이언트 라이브러리와 도구의 최신 버전을 사용하고 있습니까?
  재시도 제한 오류 및 시간 제한에 대한 지수 백오프가 포함된 재시도 정책을 사용하고 있습니까?
  재시도 애플리케이션에서 다시 시도할 수 없는 오류 발생 시에는 작업을 다시 시도하지 않습니까?
  구성 테이블 요청에 JSON을 사용하고 있습니까?
  구성 작은 요청의 성능을 향상시키기 위해 Nagle 알고리즘을 해제했습니까?
  테이블 및 파티션 데이터를 적절하게 분할했습니까?
  핫 파티션 추가 전용 및 앞에 추가 전용 패턴을 지양하고 있습니까?
  핫 파티션 여러 파티션을 대상으로 삽입/업데이트를 수행합니까?
  쿼리 범위 대부분의 경우에는 지점 쿼리를 사용하고 테이블 쿼리는 필요한 경우에만 사용하도록 스키마를 디자인했습니까?
  쿼리 밀도 일반적으로 쿼리가 애플리케이션에서 사용할 행만 스캔하여 반환합니까?
  반환되는 데이터 제한 필요하지 않은 엔터티는 반환되지 않도록 필터링을 사용하고 있습니까?
  반환되는 데이터 제한 필요하지 않은 속성은 반환되지 않도록 프로젝션을 사용하고 있습니까?
  비정규화 데이터를 가져올 때 비효율적인 쿼리 또는 여러 읽기 요청을 방지하기 위해 데이터를 비정규화했습니까?
  삽입, 업데이트 및 삭제 왕복 횟수를 줄이기 위해 동시에 수행할 수 있거나 트랜잭션 방식으로 수행해야 하는 요청을 일괄 처리하고 있습니까?
  삽입, 업데이트 및 삭제 단순한 호출 대상(삽입 또는 업데이트) 결정을 위한 엔터티 검색을 지양하고 있습니까?
  삽입, 업데이트 및 삭제 자주 함께 검색할 일련의 데이터를 여러 엔터티가 아닌 단일 엔터티에 속성으로 저장하는 것을 고려한 적이 있습니까?
  삽입, 업데이트 및 삭제 일괄 처리로 쓸 수 있으며 함께 검색할 엔터티(예: 시계열 데이터)에 대해 테이블이 아닌 Blob 사용을 고려한 적이 있습니까?

확장성 목표

애플리케이션이 확장성 목표에 도달하거나 목표를 초과하는 경우 트랜잭션 대기 시간이 길어지거나 제한이 증가할 수 있습니다. Azure Storage에서 애플리케이션을 제한하면 서비스에서 503(서버 작업 중) 또는 500(작업 시간 제한) 오류 코드를 반환하기 시작합니다. 확장성 목표의 한도 내에서 유지하여 이러한 오류를 방지하는 것은 애플리케이션의 성능을 향상시키는 데 중요한 부분입니다.

Table Service의 확장성 목표에 대한 자세한 내용은 Table Storage에 대한 확장성 및 성능 목표를 참조하세요.

최대 스토리지 계정 수

특정 구독/지역 조합에 허용되는 최대 스토리지 계정 수에 도달하는 경우 여러 스토리지 계정을 사용하여 수신, 송신, IOPS(초당 I/O 작업 수) 또는 용량을 늘리도록 분할하고 있습니까? 이 시나리오에서는 가능한 경우 워크로드에 필요한 스토리지 계정 수를 줄이기 위해 스토리지 계정에 대한 제한을 늘리는 것이 좋습니다. 스토리지 계정에 대한 제한을 늘리도록 요청하려면 Azure 지원에 문의하세요.

용량 및 트랜잭션 목표

애플리케이션이 특정 스토리지 계정의 확장성 목표에 도달한 경우 다음 방법 중 하나를 사용할 수 있습니다.

  • 애플리케이션이 확장성 목표에 도달하거나 목표를 초과한 원인이 되는 워크로드를 다시 고려합니다. 즉, 대역폭이나 용량을 더 적게 사용하거나 트랜잭션을 더 적게 수행하도록 해당 작업을 다시 디자인할 수 있는지를 파악합니다.
  • 애플리케이션에서 확장성 목표 중 하나를 초과해야 하는 경우 여러 스토리지 계정을 만들어 애플리케이션 데이터를 해당 스토리지 계정에 분할합니다. 이러한 패턴을 사용하는 경우에는 향후 부하 분산을 위해 스토리지 계정을 더 추가할 수 있도록 애플리케이션을 디자인해야 합니다. 스토리지 계정 자체에는 저장된 데이터, 수행된 트랜잭션 또는 전송된 데이터와 관련된 사용량 이외의 비용은 없습니다.
  • 애플리케이션의 대역폭 목표에 도달하는 경우 클라이언트 쪽의 데이터를 압축하여 Azure Storage에 보내는 데 필요한 대역폭을 줄이는 것이 좋습니다. 데이터를 압축하면 대역폭이 절약되고 네트워크 성능이 향상될 수 있지만 성능에 부정적인 영향을 미칠 수도 있습니다. 클라이언트 쪽의 데이터 압축 및 압축 풀기에 대한 추가 처리 요구 사항으로 인해 성능에 미치는 영향을 평가합니다. 압축된 데이터를 저장하면 표준 도구를 사용하여 데이터를 보는 것이 더 어려울 수 있으므로 문제 해결이 더 어려워 질 수 있습니다.
  • 애플리케이션에서 확장성 목표에 도달하는 경우 재시도에 대해 지수 백오프를 사용하고 있는지 확인합니다. 이 문서에서 설명하는 추천 사항을 구현하여 확장성 목표에 도달하지 않도록 하는 것이 가장 좋습니다. 그러나 지수 백오프를 재시도에 사용하면 애플리케이션에서 빠르게 다시 시도하지 못하므로 제한 성능이 저하될 수 있습니다. 자세한 내용은 시간 제한 및 서버 작업 중 오류 섹션을 참조하세요.

데이터 작업의 대상

스토리지 계정에 대한 트래픽이 증가함에 따라 Azure Storage 부하 분산이 발생하지만 트래픽이 갑자기 증가하면 이 처리량 볼륨을 즉시 얻지 못할 수 있습니다. Azure Storage가 자동으로 테이블의 부하를 분산하므로 버스트 중에 제한 및/또는 시간 제한이 표시될 것입니다. 일반적으로는 트래픽이 서서히 증가하면 시스템이 적절하게 부하를 분산할 수 있는 시간이 있으므로 더 나은 결과를 얻을 수 있습니다.

초당 엔터티 수(스토리지 계정)

계정의 테이블 액세스를 위한 확장성 한도는 초당 최대 2만 개 엔터티(각 1KB)입니다. 일반적으로는 삽입, 업데이트, 삭제 또는 스캔하는 각 엔터티가 이 목표 수 계산에 포함됩니다. 따라서 엔터티 100개가 포함된 일괄 삽입을 수행하면 엔터티가 100개로 계산됩니다. 마찬가지로 1,000개 엔터티를 스캔하여 5를 반환하는 쿼리의 경우 엔터티가 1,000개로 계산됩니다.

초당 엔터티 수(파티션)

단일 파티션 내에서 테이블 액세스를 위한 확장성 목표는 초당 2,000개 엔터티(각 1KB)입니다. 파티션의 경우에도 이전 섹션에서 설명한 것과 동일한 계산 방법이 사용됩니다.

네트워킹

애플리케이션의 실제 네트워크 제약 조건은 성능에 큰 영향을 미칠 수 있습니다. 다음 섹션에서는 사용자에게 적용될 수 있는 몇 가지 제한 사항에 대해 설명합니다.

클라이언트 네트워크 기능

다음 섹션에서 설명한 대로 네트워크 링크의 대역폭과 품질은 애플리케이션 성능에서 중요한 역할을 합니다.

처리량

대역폭의 경우에는 클라이언트 기능에 문제가 있는 경우가 많습니다. 대규모 Azure 인스턴스에는 용량이 더 많은 NIC가 포함되므로 머신 하나의 네트워크 한도를 높여야 하는 경우에는 더 큰 인스턴스나 더 많은 VM을 사용하는 것이 좋습니다. 온-프레미스 애플리케이션에서 Azure Storage에 액세스하는 경우에도 동일한 규칙이 적용됩니다. 즉, 클라이언트 디바이스의 네트워크 기능과 Azure Storage 위치에 대한 네트워크 연결을 파악하고, 필요한 경우 이를 향상시키거나 해당 기능 내에서 작동하도록 애플리케이션을 디자인해야 합니다.

네트워크 사용량과 마찬가지로 네트워크 조건으로 인해 오류와 패킷 손실이 발생하면 효과적인 처리량의 속도가 느려집니다. WireShark 또는 NetMon을 사용하면 이 문제를 진단하는 데 도움이 될 수 있습니다.

위치

모든 분산 환경에서는 클라이언트를 서버 근처에 배치하면 성능을 최대화할 수 있습니다. Azure Storage 액세스 시 대기 시간을 최소화하려는 경우에는 클라이언트를 같은 Azure 지역 내에 배치하는 것이 가장 좋습니다. 예를 들어 Azure 웹앱에서 Azure Storage를 사용하는 경우 웹앱과 스토리지를 모두 단일 하위 지역(예: 미국 서부 또는 동남 아시아) 내에 배치합니다. 리소스를 공동 배치하면 단일 지역 내의 대역폭 사용량이 무료이므로 대기 시간과 비용이 줄어듭니다.

모바일 디바이스 앱 또는 온-프레미스 엔터프라이즈 서비스와 같이 클라이언트 애플리케이션이 Azure Storage에 액세스하지만 Azure 내에서 호스팅되지 않는 경우 스토리지 계정을 해당 클라이언트와 가까운 지역에 배치하면 대기 시간을 줄일 수 있습니다. 고객이 광범위하게 분산되어 있는 경우(예: 북아메리카 및 유럽 일부) 지역당 하나의 스토리지 계정을 사용하는 것이 좋습니다. 애플리케이션에서 개별 사용자 관련 데이터를 저장하며 스토리지 계정 간에 데이터를 복제하지 않아도 되는 경우 이 방식을 보다 쉽게 구현할 수 있습니다.

SAS 및 CORS

Azure Storage의 데이터에 액세스할 수 있는 권한을 코드(예: 사용자의 웹 브라우저 또는 휴대폰 앱에서 실행되는 JavaScript)에 부여해야 한다고 가정합니다. 한 가지 방법은 프록시로 작동하는 서비스 애플리케이션을 구축하는 것입니다. 사용자의 디바이스는 서비스를 인증하여 Azure Storage 리소스에 대한 액세스 권한을 부여합니다. 이러한 방식을 사용하면 안전하지 않은 디바이스에서 스토리지 계정 키가 노출되는 상황을 방지할 수 있습니다. 그러나 이 방법은 사용자의 디바이스와 Azure Storage 간에 전송되는 모든 데이터가 서비스 애플리케이션을 통과해야 하므로 서비스 애플리케이션에 상당한 오버헤드가 발생합니다.

SAS(공유 액세스 서명)를 사용하여 서비스 애플리케이션을 Azure Storage의 프록시로 사용하지 않도록 방지할 수 있습니다. SAS를 사용하면 제한된 액세스 토큰을 사용하여 사용자의 디바이스에서 Azure Storage에 직접 요청할 수 있습니다. 예를 들어 사용자가 사진을 애플리케이션에 업로드하려는 경우 서비스 애플리케이션에서 SAS를 생성하여 사용자의 디바이스로 보낼 수 있습니다. SAS 토큰은 지정된 시간 간격 동안 Azure Storage 리소스에 쓸 수 있는 권한을 부여할 수 있으며, 그 후에는 SAS 토큰이 만료됩니다. SAS에 대한 자세한 내용은 SAS(공유 액세스 서명)를 사용하여 Azure Storage 리소스에 대한 제한된 액세스 권한 부여를 참조하세요.

일반적으로 웹 브라우저는 한 도메인의 웹 사이트에서 호스팅하는 페이지의 JavaScript에서 다른 도메인에 대한 쓰기 작업과 같은 특정 작업을 수행할 수 없습니다. 동일한 원본 정책이라고 하는 이 정책은 한 페이지의 악성 스크립트에서 다른 웹 페이지의 데이터에 액세스하지 못하도록 방지합니다. 그러나 클라우드에서 솔루션을 구축하는 경우 동일한 원본 정책이 제한될 수 있습니다. CORS(원본 간 리소스 공유)는 대상 도메인이 원본 도메인에서 시작된 요청을 신뢰하는 브라우저와 통신할 수 있게 하는 브라우저 기능입니다.

예를 들어 Azure에서 실행되는 웹 애플리케이션에서 Azure Storage 계정에 리소스를 요청한다고 가정합니다. 웹 애플리케이션은 원본 도메인이고, 스토리지 계정은 대상 도메인입니다. Azure Storage에서 원본 도메인의 요청을 신뢰하는 웹 브라우저와 통신하도록 모든 Azure Storage 서비스에 대해 CORS를 구성할 수 있습니다. CORS에 대한 자세한 내용은 Azure Storage에 대한 CORS(원본 간 리소스 공유) 지원을 참조하세요.

SAS 및 CORS는 모두 웹 애플리케이션에서 불필요한 로드를 방지하는 데 도움이 될 수 있습니다.

일괄 처리 트랜잭션

Table service는 동일한 테이블에 있고 동일한 파티션 그룹에 속한 엔터티에 대한 일괄 처리 트랜잭션을 지원합니다. 자세한 내용은 엔터티 그룹 트랜잭션 수행을 참조하세요.

.NET 구성

이 섹션에서는 .NET Framework를 사용하는 프로젝트에서 성능을 크게 개선하기 위해 사용할 수 있는 몇 가지 빠른 구성 설정을 소개합니다. .NET 이외의 언어를 사용하는 경우에는 선택한 언어에 비슷한 개념이 적용되는지를 확인하세요.

기본 연결 제한 늘리기

참고 항목

연결 풀링은 ServicePointManager 클래스에서 제어하므로 이 섹션은 .NET Framework를 사용하는 프로젝트에 적용됩니다. .NET Core는 연결 풀 관리와 관련하여 상당한 변화를 도입했습니다. 여기서 연결 풀은 HttpClient 수준에서 발생하고 풀 크기는 기본적으로 제한되지 않습니다. 즉, HTTP 연결은 워크로드를 충족하도록 자동으로 크기가 조정됩니다. 성능 향상을 활용하려면 가능한 경우 최신 버전의 .NET을 사용하는 것이 좋습니다.

.NET Framework를 사용하는 프로젝트에서는 다음 코드를 사용하여 기본 연결 제한(일반적으로 클라이언트 환경에서는 2이고, 서버 환경에서는 10임)을 100으로 늘릴 수 있습니다. 일반적으로 이 값을 애플리케이션에서 사용되는 대략적인 스레드 수로 설정해야 합니다. 연결을 열기 전에 연결 제한을 설정합니다.

ServicePointManager.DefaultConnectionLimit = 100; //(Or More)  

.NET Framework의 연결 풀 제한에 대한 자세한 내용은 .NET Framework 연결 풀 제한 및 .NET용 새 Azure SDK를 참조하세요.

기타 프로그래밍 언어의 경우 설명서에서 연결 제한을 설정하는 방법을 확인하세요.

최소 스레드 수 늘리기

비동기 작업에서 동기 호출을 함께 사용하는 경우 스레드 풀의 스레드 수를 늘릴 수 있습니다.

ThreadPool.SetMinThreads(100,100); //(Determine the right number for your application)  

자세한 내용은 ThreadPool.SetMinThreads 메서드를 참조하세요.

무제한 병렬 처리

병렬 처리는 성능에 매우 유용할 수 있지만, 스레드 수 또는 병렬 처리 요청 수가 제한되지 않는 무제한 병렬 처리를 사용하는 데 주의해야 합니다. 데이터를 업로드하거나 다운로드하거나, 동일한 스토리지 계정의 여러 파티션에 액세스하거나, 동일한 파티션의 여러 항목에 액세스하기 위한 병렬 요청은 제한해야 합니다. 무제한 병렬 처리인 경우 애플리케이션에서 클라이언트 디바이스의 기능 또는 스토리지 계정의 확장성 목표를 초과하여 대기 시간과 제한이 길어질 수 있습니다.

클라이언트 라이브러리 및 도구

최상의 성능을 위해 항상 Microsoft에서 제공하는 최신 클라이언트 라이브러리와 도구를 사용합니다. Azure Storage 클라이언트 라이브러리는 다양한 언어로 사용할 수 있습니다. 또한 Azure Storage는 PowerShell 및 Azure CLI도 지원합니다. Microsoft는 성능을 고려하여 이러한 클라이언트 라이브러리와 도구를 적극적으로 개발하고, 최신 서비스 버전으로 해당 도구를 최신 상태로 유지하며, 이를 통해 검증된 여러 성능 사례를 내부적으로 처리하고 있습니다.

서비스 오류 처리

서비스에서 요청을 처리할 수 없는 경우 Azure Storage는 오류를 반환합니다. 지정된 시나리오에서 Azure Storage가 반환하는 오류를 이해하면 성능을 최적화하는 데 도움이 됩니다.

시간 제한 및 서버 사용 중 오류

확장성 제한에 도달하면 Azure Storage에서 애플리케이션을 제한할 수 있습니다. Azure Storage에서 일시적인 조건으로 인해 요청을 처리하지 못하는 경우도 있습니다. 두 경우 모두 서비스에서 503(서버 작업 중) 또는 500(시간 제한) 오류를 반환할 수 있습니다. 이러한 오류는 서비스에서 처리량을 높이기 위해 데이터 파티션을 균형 있게 다시 분산시키는 경우에도 발생할 수 있습니다. 클라이언트 애플리케이션은 일반적으로 이러한 오류 중 하나가 발생되는 작업을 다시 시도해야 합니다. 그러나 애플리케이션에서 확장성 목표를 초과하여 Azure Storage에서 애플리케이션을 제한하는 경우 또는 서비스에서 다른 이유로 요청을 처리할 수 없었던 경우에도 적극적인 재시도로 인해 문제가 악화될 수 있습니다. 지수 백오프 재시도 정책을 사용하는 것이 좋으며, 클라이언트 라이브러리는 기본적으로 이 동작으로 설정됩니다. 예를 들어 애플리케이션은 작업을 2초, 4초, 10초, 30초 후에 다시 시도한 다음, 계속 실패하면 작업을 완전히 포기할 수 있습니다. 이렇게 하면 애플리케이션에서 제한으로 이어질 수 있는 동작을 악화시키는 대신 서비스에 대한 부하를 크게 줄입니다.

연결 오류는 제한으로 인해 발생하는 것이 아니라 일시적이므로 즉시 다시 시도할 수 있습니다.

다시 시도할 수 없는 오류

클라이언트 라이브러리는 다시 시도할 수 있는 오류와 다시 시도할 수 없는 오류를 인식하여 재시도를 처리합니다. 그러나 Azure Storage REST API를 직접 호출하는 경우에는 다시 시도하지 않아야 하는 오류가 있습니다. 예를 들어 400(잘못된 요청) 오류는 클라이언트 애플리케이션에서 예상되지 않은 형식으로 인해 처리할 수 없는 요청을 보냈음을 나타냅니다. 이 요청을 다시 보내면 매번 동일한 응답이 발생하므로 다시 시도할 필요가 없습니다. Azure Storage REST API를 직접 호출하는 경우 잠재적 오류와 재시도 여부를 알고 있어야 합니다.

Azure Storage 오류 코드에 대한 자세한 내용은 상태 및 오류 코드를 참조하세요.

구성

이 섹션에는 Table service에서 성능을 크게 향상시키는 데 사용할 수 있는 다양한 빠른 구성 설정이 나와 있습니다.

JSON 사용

2013-08-15 스토리지 서비스 버전부터 Table service는 테이블 데이터를 전송하는 데 XML 기반 AtomPub 형식 대신 JSON을 사용하도록 지원합니다. JSON을 사용하면 페이로드 크기를 최대 75%까지 줄일 수 있으며 애플리케이션의 성능을 크게 향상시킬 수 있습니다.

자세한 내용은 Microsoft Azure 테이블: JSON 소개Table Service 작업의 페이로드 형식을 참조하세요.

Nagle 사용 안 함

Nagle 알고리즘은 네트워크 성능을 개선하기 위한 수단으로 TCP/IP 네트워크에서 광범위하게 구현됩니다. 그러나 대화형 작업을 많이 수행하는 환경 등 일부 상황에서는 이 알고리즘이 적합하지 않습니다. Nagle의 알고리즘은 Azure Table service에 대한 요청의 성능에 부정적인 영향을 미치며, 가능하면 이를 사용하지 않도록 설정해야 합니다.

스키마

Table service의 성능에 영향을 미치는 가장 중요한 단일 요인은 데이터를 표시하고 쿼리하는 방법입니다. 각 애플리케이션별로 다르기는 하지만 이 섹션에서는 다음 항목과 관련된 일반적인 검증된 작업 방식 중 몇 가지에 대해 간략하게 설명합니다.

  • 테이블 디자인
  • 효율적인 쿼리
  • 효율적인 데이터 업데이트

테이블 및 파티션

테이블은 파티션으로 구분됩니다. 파티션에 저장되는 모든 엔터티는 같은 파티션 키를 공유하며 해당 파티션 내에서 엔터티를 식별하는 데 사용되는 고유한 행 키를 포함합니다. 파티션을 사용하는 경우에는 이점도 있지만 확장성 제한도 적용됩니다.

  • 이점: 최대 100개의 개별 스토리지 작업(총 크기 제한 4MB)을 포함하는 단일 원자성 일괄 처리 트랜잭션에서 같은 파티션의 엔터티를 업데이트할 수 있습니다. 또한 같은 수의 엔터티를 검색한다고 가정할 때 여러 파티션에 분산된 데이터보다 단일 파티션 내의 데이터를 더 효율적으로 쿼리할 수 있습니다. 단, 테이블 데이터 쿼리와 관련된 추가 권장 사항을 확인해야 합니다.
  • 확장성 제한: 파티션은 원자성 일괄 처리 트랜잭션을 지원하므로 단일 파티션에 저장된 엔터티에 대한 액세스는 부하 분산할 수 없습니다. 이러한 이유로 개별 테이블 파티션의 확장성 목표는 전체적으로 Table service의 목표보다 낮습니다.

테이블과 파티션의 이러한 특성을 고려할 때 다음과 같은 디자인 원칙을 적용해야 합니다.

  • 클라이언트 애플리케이션이 자주 업데이트하거나 동일한 파티션에서 동일한 논리적 작업 단위로 쿼리하는 데이터를 찾습니다. 예를 들어 애플리케이션에서 쓰기를 집계하거나 원자성 일괄 처리 작업을 수행하는 경우 동일한 파티션에서 데이터를 찾습니다. 또한 단일 쿼리에서는 여러 파티션에 분산된 데이터보다 단일 파티션의 데이터를 더 효율적으로 쿼리할 수 있습니다.
  • 클라이언트 애플리케이션이 별도의 파티션에서 동일한 논리적 작업 단위(즉, 단일 쿼리 또는 일괄 처리 업데이트)로 삽입, 업데이트 또는 쿼리하지 않는 데이터를 찾습니다. 단일 테이블의 파티션 키 수는 제한되지 않으므로 수백만 개의 파티션 키가 있어도 문제가 되지 않으며 성능에도 영향을 미치지 않습니다. 예를 들어 애플리케이션이 사용자가 로그인하는 유명 웹 사이트인 경우에는 사용자 ID를 파티션 키로 사용하면 효율적일 수 있습니다.

핫 파티션

핫 파티션은 계정에 대해 부적합한 비율의 트래픽을 받으며, 단일 파티션이므로 부하를 분산할 수 없는 파티션입니다. 일반적으로는 다음의 두 가지 방식 중 하나로 핫 파티션을 만듭니다.

추가 전용 및 앞에 추가 전용 패턴

“추가 전용” 패턴은 지정된 파티션 키에 대한 모든 트래픽 또는 거의 모든 트래픽이 현재 시간에 따라 증가하고 감소하는 패턴입니다. 예를 들어 애플리케이션에서 현재 날짜를 로그 데이터의 파티션 키로 사용한다고 가정합니다. 이 디자인으로 인해 모든 삽입 항목이 테이블의 마지막 파티션으로 이동하여 시스템에서 부하 분산을 올바르게 수행할 수 없습니다. 해당 파티션에 대한 트래픽의 양이 파티션 수준 확장성 목표를 초과하면 제한이 적용됩니다. 따라서 테이블 전체에서 요청을 부하 분산할 수 있도록 트래픽이 여러 파티션으로 전송되도록 하는 것이 더 효율적입니다.

트래픽이 많은 데이터

사용 중인 파티션 구성표로 인해 단일 파티션에 다른 파티션보다 훨씬 많이 사용되는 데이터만 포함되는 경우에도 해당 파티션이 단일 파티션의 확장성 목표에 도달하면 제한이 적용될 수 있습니다. 따라서 파티션 구성표로 인해 확장성 목표에 도달하는 단일 파티션이 없는지를 확인하는 것이 좋습니다.

쿼리

이 섹션에서는 Table service 쿼리에 대해 검증된 사례에 대해 설명합니다.

쿼리 범위

여러 가지 방법으로 쿼리할 엔터티의 범위를 지정할 수 있습니다. 다음 목록에서는 쿼리 범위에 대한 각 옵션에 대해 설명합니다.

  • 지점 쿼리: 지점 쿼리는 검색할 엔터티의 파티션 키와 행 키를 모두 지정하여 정확히 하나의 엔터티를 검색합니다. 이러한 쿼리는 효율적이므로 가능한 경우 항상 사용해야 합니다.
  • 파티션 쿼리: 파티션 쿼리는 공통 파티션 키를 공유하는 데이터 세트를 검색하는 쿼리입니다. 일반적으로 이 쿼리에서는 파티션 키와 함께 일부 엔터티 속성의 값 범위나 행 키 값의 범위를 지정합니다. 이러한 쿼리는 지점 쿼리보다 효율성이 낮으므로 가능하면 사용하지 않습니다.
  • 테이블 쿼리: 테이블 쿼리는 공통 파티션 키를 공유하지 않는 엔터티 세트를 검색하는 쿼리입니다. 이러한 쿼리는 효율적이지 않으므로 가능하면 사용하지 않아야 합니다.

일반적으로 스캔(단일 엔터티보다 큰 쿼리)은 수행하지 않는 것이 좋지만 스캔을 해야 하는 경우에는 불필요한 대량의 엔터티를 스캔하거나 반환하지 않도록 필요한 데이터만 스캔하도록 데이터를 구성합니다.

쿼리 밀도

쿼리 효율성에 영향을 주는 또 다른 중요한 요인은 반환되는 집합을 찾기 위해 스캔한 엔터티 수와 비교한 반환된 엔터티 수입니다. 애플리케이션에서 데이터 중 1%만 공유하는 속성 값에 대한 필터를 사용해 테이블 쿼리를 수행하는 경우 쿼리는 반환하는 1개의 엔터티당 100개의 엔터티를 스캔합니다. 앞에서 설명한 테이블 확장성 목표는 모두 반환되는 엔터티 수가 아니라 검사되는 엔터티 수와 관련이 있습니다. 원하는 엔터티를 검색하기 위해 너무 많은 엔터티를 검사해야 하므로 쿼리 밀도가 낮으면 Table service에서 애플리케이션을 쉽게 제한할 수 있습니다. 제한을 방지하는 방법에 대한 자세한 내용은 비정규화 섹션을 참조하세요.

반환되는 데이터 양 제한

쿼리에서 클라이언트 애플리케이션에 필요하지 않은 엔터티가 반환되는 경우 필터를 사용하여 반환되는 세트의 크기를 줄이는 것이 좋습니다. 클라이언트로 반환되지 않는 엔터티도 확장성 목표 계산에 포함되기는 하지만, 클라이언트 애플리케이션이 처리해야 하는 엔터티의 수와 네트워크 페이로드 크기가 감소하므로 애플리케이션의 성능은 개선됩니다. 확장성 목표는 검사되는 엔터티의 수와 관련이 있으므로 반환되는 엔터티가 많지 않은 경우에도 많은 엔터티를 필터링하는 쿼리가 여전히 제한될 수 있습니다. 쿼리를 효율적으로 만드는 방법에 대한 자세한 내용은 쿼리 밀도 섹션을 참조하세요.

클라이어트 애플리케이션에 테이블 내 엔터티의 제한된 속성 집합만 필요한 경우에는 프로젝션을 사용하여 반환되는 데이터 세트의 크기를 제한할 수 있습니다. 필터링과 마찬가지로 프로젝션을 통해 네트워크 로드 및 클라이언트 처리를 줄일 수 있습니다.

비정규화

관계형 데이터베이스를 사용할 때와는 달리, 테이블 데이터를 효율적으로 쿼리하는 검증된 작업 방식에서는 데이터를 비정규화해야 합니다. 즉, 대량의 엔터티를 스캔하여 애플리케이션에 필요한 데이터를 찾는 대신 여러 엔터티에서 같은 데이터를 복제(데이터를 찾는 데 사용할 수 있는 각 키에 대해 하나씩)하여 클라이언트에 필요한 데이터를 찾기 위해 쿼리가 스캔해야 하는 엔터티의 수를 최소화해야 합니다. 예를 들어 전자 상거래 웹 사이트에서는 고객 ID(특정 고객의 주문 정보 확인) 및 날짜(특정 날짜의 주문 정보 확인)를 모두 기준으로 사용하여 주문으로 찾을 수 있습니다. Table Storage에서는 엔터티 또는 엔터티에 대한 참조를 두 번 저장하는 것이 가장 좋습니다. 방금 설명한 예의 경우 엔터티를 고객 ID별로 쉽게 찾을 수 있도록 테이블 이름/PK/RK와 함께 한 번, 그리고 날짜별로 쉽게 찾을 수 있도록 다시 한 번 저장하는 것이 좋습니다.

삽입, 업데이트 및 삭제

이 섹션에서는 Table service에 저장된 엔터티를 수정하는 방법에 대해 검증된 사례를 설명합니다.

일괄 처리

일괄 처리 트랜잭션을 Azure Storage에서 엔터티 그룹 트랜잭션이라고 합니다. 엔터티 그룹 트랜잭션 내의 모든 작업은 단일 테이블의 단일 파티션에 있어야 합니다. 가능한 경우 엔터티 그룹 트랜잭션을 사용하여 삽입, 업데이트 및 삭제를 일괄 처리 방식으로 수행합니다. 엔터티 그룹 트랜잭션을 사용하면 클라이언트 애플리케이션에서 서버로의 왕복 횟수와 청구 가능한 트랜잭션의 수가 줄어들고(엔터티 그룹 트랜잭션이 청구 목적으로 단일 트랜잭션으로 계산되며, 최대 100개의 스토리지 작업을 포함할 수 있음), 원자성 업데이트를 사용하도록 설정됩니다(엔터티 그룹 트랜잭션 내에서 모든 작업이 성공하거나 실패함). 모바일 디바이스와 같이 대기 시간이 긴 환경에서는 엔터티 그룹 트랜잭션을 사용하면 큰 이점을 얻을 수 있습니다.

Upsert

가능한 경우에는 항상 테이블 Upsert 작업을 사용합니다. Upsert에는 두 가지 형식이 있으며, 두 형식 모두 기존의 InsertUpdate 작업보다는 비효율적일 수 있습니다.

  • InsertOrMerge: 엔터티 속성의 하위 집합을 업로드하려고 하지만 엔터티가 이미 있는지 여부가 확실하지 않은 경우 이 작업을 사용합니다. 엔터티가 있는 경우 이 작업을 수행하면 Upsert 작업에 포함된 속성은 업데이트되고 기존의 모든 속성은 그대로 유지됩니다. 엔터티가 없으면 새 엔터티가 삽입됩니다. 이 작업은 변경되는 속성만 업데이트하면 되므로 쿼리에서 프로젝션을 사용하는 것과 비슷합니다.
  • InsertOrReplace: 완전히 새로운 엔터티를 업로드하려고 하지만 엔터티가 이미 있는지 여부가 확실하지 않은 경우 이 작업을 사용합니다. 새로 업로드된 엔터티가 이전 엔터티를 완전히 덮어쓰므로 완전히 올바른 엔터티임을 알고 있는 경우 이 작업을 사용합니다. 예를 들어 애플리케이션이 사용자의 위치 데이터를 이전에 저장했는지 여부에 관계없이 사용자의 현재 위치를 저장하는 엔터티를 업데이트하려고 하며, 새 위치 엔터티가 완전하고 이전 엔터티의 정보는 전혀 필요하지 않은 경우 이 작업을 수행할 수 있습니다.

단일 엔터티에 데이터 계열 저장

경우에 따라 애플리케이션은 자주 한꺼번에 검색해야 하는 데이터 계열을 저장합니다. 예를 들어 애플리케이션은 지난 24시간의 데이터에 대한 롤링 차트를 그리기 위해 시간에 따른 CPU 사용량을 추적할 수 있습니다. 이 경우 사용할 수 있는 한 가지 방법은 시간당 테이블 엔터티 하나를 저장하는 것입니다. 이때 각 엔터티는 특정 시간을 나타내며 해당 시간의 CPU 사용량을 저장합니다. 이 데이터를 그리려면 애플리케이션이 가장 최근의 24시간에 해당하는 데이터를 포함하는 엔터티를 검색해야 합니다.

또는 애플리케이션은 단일 엔터티의 별도 속성으로 매시간의 CPU 사용량을 저장할 수 있습니다. 매시간을 업데이트하기 위해 애플리케이션은 단일 InsertOrMerge Upsert 호출을 사용하여 최근 시간에 대한 값을 업데이트할 수 있습니다. 데이터를 그리기 위해 애플리케이션에서 24시간 동안의 엔터티가 아니라 하나의 엔터티만 검색하면 되므로 효율적인 쿼리를 만들 수 있습니다. 쿼리 효율성에 대한 자세한 내용은 쿼리 범위 섹션을 참조하세요.

Blob에 정형 데이터 저장

일괄 처리 삽입을 수행한 다음, 엔터티 범위를 함께 검색하는 경우 테이블 대신 Blob을 사용하는 것이 좋습니다. 로그 파일을 예로 들 수 있습니다. 로그를 몇 분 동안 일괄 처리하고, 삽입한 다음, 한 번에 몇 분 동안 검색할 수 있습니다. 이 경우 테이블 대신 Blob을 사용하면 쓰거나 읽을 개체 수와 필요한 요청 수를 크게 줄일 수 있으므로 성능이 향상됩니다.

다음 단계