인덱스 테이블 패턴

데이터 저장소가 적절한 보조 인덱스를 제공하지 않는 경우 애플리케이션이 쿼리에서 자주 사용하는 데이터에 대한 별도의 조회 테이블을 만들고 유지 관리합니다. 이 방법은 쿼리가 기본 키 또는 파티션 키를 사용하지 않는 경우 전체 데이터 검색을 방지하여 읽기 성능을 향상시킵니다.

컨텍스트 및 문제점

많은 데이터 저장소는 기본 키를 사용하여 엔터티 컬렉션에 대한 데이터를 구성합니다. 애플리케이션은 이 키를 사용하여 데이터를 찾고 검색할 수 있습니다. 다음 그림에서는 기본 키인 고객 ID로 구성된 고객 정보를 보관하는 데이터 저장소의 예를 보여줍니다.

기본 키인 고객 ID로 구성된 고객 데이터 테이블을 보여 주는 다이어그램 고객 데이터 열에는 고객 성 및 마을이 포함됩니다.

이 이미지는 고객 레코드의 2열 테이블을 보여줍니다. 첫 번째 열인 고객 ID(기본 키)에는 고객 ID 1부터 9까지의 줄임표, ID 1000 및 다른 줄임표가 포함됩니다. 두 번째 열인 Customer Data에는 성 및 마을과 줄임표로 구성된 고객 데이터가 포함됩니다. 눈에 띄는 예로는 성 스미스와 레드먼드 마을에 매핑되는 ID 1과 성 존스와 시애틀 마을에 매핑되는 ID 2가 있습니다. 또 다른 행은 시카고의 스미스를 보여주고, 또 다른 행은 레드먼드의 스미스를 보여줍니다. 또 다른 행에는 시카고에 있는 존스가 표시됩니다.

기본 키는 이 키의 값을 기반으로 데이터를 가져오는 쿼리에 유용하지만 다른 필드를 기반으로 데이터를 검색해야 하는 애플리케이션은 해당 쿼리에 기본 키를 사용할 수 없습니다. 고객 예제에서 애플리케이션은 고객이 있는 마을과 같은 다른 특성의 값을 참조하여 데이터를 쿼리하는 경우 고객 ID 기본 키를 사용하여 고객을 검색할 수 없습니다. 마을만 참조하는 쿼리를 수행하려면 애플리케이션이 모든 고객 레코드를 가져와서 검사해야 할 수 있습니다. 이는 느린 프로세스일 수 있습니다.

많은 관계형 데이터베이스 관리 시스템은 보조 인덱스를 지원합니다. 보조 인덱스는 하나 이상의 기본이 아닌(보조) 키 필드로 구성된 별도의 데이터 구조입니다. 보조 인덱스는 인덱싱된 각 값의 데이터가 저장되는 위치를 나타냅니다. 보조 인덱스의 항목은 일반적으로 보조 키의 값을 기준으로 정렬되어 데이터를 빠르게 조회할 수 있습니다. 데이터베이스 관리 시스템은 일반적으로 이러한 인덱스를 자동으로 유지 관리합니다.

관계형 데이터베이스를 사용하면 여러 보조 인덱스가 다양한 쿼리 패턴을 지원할 수 있습니다. 예를 들어 고객 ID가 기본 키인 관계형 데이터베이스의 Customers 테이블에서 애플리케이션이 상주하는 마을에서 고객을 자주 조회하는 경우 타운 필드에 보조 인덱스 추가가 유용합니다.

보조 인덱스는 관계형 시스템에서 일반적이지만 모든 데이터 저장소가 동등한 기능을 제공하는 것은 아닙니다. 일부 데이터 저장소에는 보조 인덱스가 없는 반면 다른 데이터 저장소는 워크로드의 쿼리, 분할 또는 성능 요구 사항을 충족하지 않는 인덱스를 제공합니다. 이러한 경우 애플리케이션은 전체 검사와 수동 인덱스 관리 중에서 선택해야 합니다.

해결 방법

지정된 키를 사용하여 데이터를 구성하는 인덱스 테이블을 만듭니다. 다음 섹션에서는 인덱스 테이블을 구조화하기 위한 세 가지 일반적인 전략에 대해 설명합니다. 다음 두 섹션에서는 특정 시나리오에서 인덱스 테이블의 용도에 대해 설명합니다.

표준 구조화 전략

다음 전략은 일반적으로 인덱스 테이블을 구성하는 데 사용됩니다. 시나리오에 필요한 보조 인덱스 수와 애플리케이션이 수행하는 쿼리의 특성에 따라 전략을 선택합니다.

완전 비정규화

전체 비정규화는 각 인덱스 테이블의 데이터를 복제하지만 기본 키가 아닌 키로 구성합니다. 다음 그림에서는 Town 및 LastName별로 동일한 고객 정보를 구성하는 인덱스 테이블을 보여 줍니다.

고객 데이터를 복제하고 Town 및 LastName 키로 구성하는 인덱스 테이블의 다이어그램

이 전략은 데이터가 자주 변경되지 않는 읽기가 많은 워크로드에 적합합니다. 업데이트 속도가 증가함에 따라 모든 복사본을 유지 관리하면 처리 오버헤드가 추가됩니다( 일관성 복잡성 참조). 대용량 데이터 세트의 경우 복사본을 저장하려면 상당한 공간이 필요할 수도 있습니다.

정규화된 인덱스

정규화된 인덱스 테이블은 기본 키가 아닌 키별로 데이터를 구성합니다. 정규화된 인덱스 테이블은 다음 그림과 같이 해당 데이터를 복제하는 대신 기본 키를 사용하여 원래 데이터를 참조합니다. 원래 데이터를 팩트 테이블이라고 합니다.

Tip

여기서 팩트 테이블 은 인덱스가 참조하는 신뢰할 수 있는 원본 테이블을 의미합니다. 차원 데이터 모델을 의미하지는 않습니다.

데이터를 복제하는 대신 기본 키로 팩트 테이블을 참조하는 정규화된 인덱스 테이블의 다이어그램

이 기법은 공간을 절약하고 복제 데이터를 유지하는 오버헤드를 줄인다는 장점이 있습니다. 단점은 애플리케이션이 보조 키를 사용하여 데이터를 찾기 위해 두 가지 조회 작업을 수행해야 한다는 것입니다. 애플리케이션은 인덱스 테이블에서 데이터에 대한 기본 키를 찾은 다음 기본 키를 사용하여 팩트 테이블의 데이터를 조회해야 합니다.

부분 비정규화

부분 비정규화는 자주 검색되는 필드를 복제하고 기본 키가 아닌 키로 구성되는 인덱스 테이블을 만듭니다. 팩트 테이블은 액세스 빈도가 낮은 필드에 액세스하기 위해 참조됩니다. 다음 그림에서는 일반적으로 액세스하는 데이터가 각 인덱스 테이블에서 중복되는 방법을 보여 있습니다.

자주 액세스하는 필드를 복제하고 나머지 데이터에 대한 팩트 테이블을 참조하는 부분적으로 정규화된 인덱스 테이블의 다이어그램

이 전략은 처음 두 가지 방법의 균형을 조정합니다. 단일 조회를 사용하여 일반적인 쿼리에 대한 데이터를 신속하게 검색할 수 있지만 공간 및 유지 관리 오버헤드는 전체 데이터 세트를 복제하는 것만큼 중요하지 않습니다.

복합 키

일부 애플리케이션은 값의 조합을 지정하여 데이터를 자주 쿼리합니다(예: "Redmond에 살고 있고 성이 Smith인 모든 고객 찾기"). 이 경우 이 경우 Town 및 LastName 특성과 같은 여러 특성에서 인덱스 키를 만듭니다.

서로 다른 값 조합이 동일한 키를 생성할 수 없도록 구성 요소 경계를 유지하는 인코딩을 사용합니다. 쿼리가 키 순서에 따라 달라지는 경우 인코딩이 데이터 저장소의 키 데이터 정렬 규칙에 따라 필요한 정렬 순서를 유지해야 합니다.

다음 그림에서는 복합 키를 기반으로 하는 인덱스 테이블을 보여줍니다. 키는 Town을 기준으로 정렬되며, Town 값이 같은 레코드는 LastName을 기준으로 정렬됩니다.

인덱스 테이블 및 팩트 테이블의 다이어그램 인덱스 테이블은 Town 및 LastName 특성을 연결하여 형성된 복합 키로 구성됩니다.

다이어그램은 왼쪽의 인덱스 테이블과 오른쪽에 팩트 테이블을 포함합니다. 팩트 테이블은 고객 데이터를 기본 키인 고객 ID별로 구성합니다. 각 고객 데이터 행에는 고객 성, 마을 및 다른 데이터를 나타내는 줄임표가 포함됩니다. 인덱스 테이블은 Town과 LastName을 결합한 복합 키로 구성됩니다. 두 번째 열인 고객 참조(ID) 및 일반적으로 쿼리되는 데이터에는 고객 ID와 줄임표가 포함됩니다. 화살표는 인덱스 테이블 행에서 팩트 테이블까지 확장되며, 각 화살표는 해당 인덱스 항목에서 참조하는 고객 ID 행을 가리킵니다. 교차 화살표는 복합 키로 정렬된 항목이 팩트 테이블의 여러 위치에서 고객 행을 참조할 수 있음을 보여 줍니다.

분할된 데이터에 대한 테이블 인덱스

인덱스 테이블은 분할된 데이터에 대한 쿼리 작업의 속도를 높일 수 있습니다. 샤드 키가 해시될 때 특히 유용합니다. 다음 그림은 샤드 키가 Customer ID를 해시한 값인 예를 보여 줍니다. 인덱스 테이블은 해시되지 않은 값인 Town 및 LastName별로 항목을 구성하고 각 항목에 해당하는 해시된 분할 키를 저장합니다.

이 구성은 해시되지 않은 값에 대한 범위 조회와 순서 기반 조회를 지원하는 동시에, 각 레코드를 올바른 샤드에서 검색하는 데 필요한 라우팅 정보를 제공합니다. 예를 들어 "Redmond에 있는 모든 고객 찾기"와 같은 쿼리는 인덱스 테이블의 연속 블록에서 일치하는 항목을 찾을 수 있습니다. 그런 다음 애플리케이션은 인덱스 테이블에 저장된 샤드 키를 사용하여 고객 데이터 참조를 따라갑니다.

해시되지 않은 값을 해시된 분할 키에 매핑하여 분할된 데이터에 대한 빠른 조회를 제공하는 분할된 데이터 테이블 및 인덱스 테이블의 다이어그램

문제 및 고려 사항

이 패턴을 구현하는 방법을 결정할 때 다음 사항을 고려합니다.

  • 유지관리 오버헤드. 보조 인덱스를 유지 관리하면 상당한 오버헤드가 발생할 수 있습니다. 애플리케이션에서 사용하는 쿼리를 분석하고 이해합니다. 정기적으로 사용할 가능성이 있는 경우에만 인덱스 테이블을 만듭니다. 애플리케이션이 가끔씩만 수행하거나 수행하지 않는 쿼리를 지원하기 위해 투기적 인덱스 테이블을 만들지 마세요. 쿼리 패턴 수가 증가함에 따라 인덱스 테이블의 수가 증가하고 각 테이블은 모니터링, 유지 관리 및 디버그할 작업 노출 영역을 추가합니다. 각 인덱스 테이블의 쿼리 이점을 다른 파생 데이터 구조 유지 관리의 운영 비용과 비교합니다. 기존 인덱스 테이블을 주기적으로 검토하여 더 이상 쿼리되지 않는 테이블을 식별하고 제거합니다.

  • 스토리지 및 처리량 비용. 인덱스 테이블에서 데이터를 복제하면 인덱스 테이블 수와 복사된 필드 크기에 비례하여 스토리지 비용이 증가합니다. 여러 데이터 복사본을 유지 관리하면 노력도 추가됩니다. 인덱스 테이블에 대한 모든 쓰기 작업은 처리량 용량을 소모하며, 이는 Azure Table Storage에서는 스토리지 계정 한도에 반영되는 트랜잭션이고 Azure Cosmos DB에서는 요청 단위(RU)입니다. 비용은 스토리지를 넘어 쓰기 쪽 처리량까지 확장됩니다.

  • 이중 조회 페널티. 인덱스 테이블을 원래 데이터를 참조하는 정규화된 구조로 구현하려면 애플리케이션에서 데이터를 찾기 위해 두 가지 조회 작업을 수행해야 합니다. 첫 번째 작업은 인덱스 테이블을 검색하여 기본 키를 검색하고, 두 번째 작업은 기본 키를 사용하여 데이터를 가져옵니다.

  • 일관성 복잡성. 시스템에서 큰 데이터 세트에 여러 인덱스 테이블을 통합하는 경우 인덱스 테이블과 원래 데이터 간의 일관성을 유지하기가 어려울 수 있습니다. 원본 데이터 및 인덱스 항목을 동일한 트랜잭션에서 업데이트할 수 없는 경우 최종 일관성 모델을 중심으로 애플리케이션을 디자인합니다. 쓰기를 승인하기 전에 각 원본 변경 내용을 지속적으로 캡처합니다. 예를 들어 데이터베이스 변경 피드를 사용하거나 트랜잭션 아웃박스 패턴을 사용하여 원본 변경과 동일한 트랜잭션에 아웃박스 레코드를 작성하거나 원본 데이터를 변경하기 전에 명령을 큐에 추가합니다. 명령 기반 접근 방식에서 작업자를 사용하여 명령을 처리하고 원본 데이터 및 해당 인덱스를 업데이트합니다. 원본 데이터를 업데이트한 다음 인덱스 업데이트 메시지를 독립적으로 게시하지 마세요. 이러한 작업 간의 오류로 인해 인덱스가 부실해질 수 있기 때문입니다.

    메시지 배달 및 재시도로 인해 동일한 업데이트가 두 번 이상 실행될 수 있으므로 비동기 소비자를 idempotent로 디자인합니다. 동일한 원본 레코드에 대한 업데이트는 순서가 맞지 않게 도착할 수도 있습니다. 각 인덱스 업데이트에 원본 버전 또는 시퀀스 번호를 포함하고 인덱스의 버전보다 최신인 경우에만 업데이트를 적용합니다. 삭제 작업의 경우, 지연된 오래된 업데이트가 삭제된 인덱스 항목을 다시 생성하지 못하도록 버전 정보가 있는 삭제 표시 또는 그에 준하는 상한 워터마크를 유지합니다. 원본 데이터 쓰기와 비동기 인덱스 업데이트 사이의 간격 동안 인덱스 테이블에 대한 쿼리는 원본 데이터에서 업데이트되거나 삭제된 레코드에 대한 부실 참조를 반환할 수 있으며 최근에 추가된 레코드를 생략할 수 있습니다.

  • 인덱스 테이블 분할 인덱스 테이블 자체도 파티셔닝되거나 샤딩될 수 있으며, 이로 인해 쿼리 라우팅이 더 복잡해지고 분할 전략은 인덱스가 지원하도록 설계된 쿼리 패턴에 부합해야 합니다.

이 패턴을 사용하는 경우

애플리케이션이 기본(또는 분할된 데이터베이스) 키가 아닌 키를 사용하여 데이터를 자주 검색해야 하는 경우가 자주 있고 데이터 저장소가 기본적으로 보조 인덱스를 지원하지 않거나 기본 인덱스가 워크로드의 쿼리, 분할 또는 성능 요구 사항을 충족하지 않는 경우 이 패턴을 사용합니다.

이 패턴은 다음과 같은 경우에 적합하지 않을 수 있습니다.

  • 데이터가 휘발성인 경우. 데이터가 너무 자주 변경되어 쓰기 속도가 인덱스 테이블을 비동기적으로 새로 고칠 수 있는 속도를 초과합니다. 데이터 최신성이 보장되지 않는 기간이 인덱스가 영구적으로 최신 상태를 잃을 때까지 계속 늘어나므로 인덱스는 효과가 없어지고, 인덱스 테이블을 유지 관리하는 데 드는 스토리지 및 처리량 측면의 오버헤드가 쿼리 비용 절감분보다 커집니다.

  • 구분되지 않는 키가 있습니다. 인덱스 테이블의 보조 키로 선택한 필드는 차별이 없으며 작은 값 집합만 가질 수 있습니다(예: 항목이 활성 상태인지 여부를 기록하는 부울 필드). 인덱스 테이블은 전체 스토리지 및 처리량 비용을 발생하지만 최소한의 쿼리 선택성을 제공합니다.

  • 데이터 값에는 분산이 왜곡되어 있습니다. 인덱스 테이블의 보조 키로 선택한 필드에 대한 데이터 값의 균형은 매우 왜곡됩니다. 예를 들어 레코드의 90%가 필드에 동일한 값을 포함하는 경우 이 필드를 기반으로 데이터를 조회하기 위해 인덱스 테이블을 만들고 유지 관리하면 데이터를 순차적으로 검사하는 것보다 오버헤드가 더 많이 발생할 수 있습니다. 그러나 쿼리가 나머지 10%에 있는 값을 자주 대상으로 하는 경우 이 인덱스는 여전히 유용할 수 있습니다.

워크로드 디자인

설계자는 워크로드 디자인에서 인덱스 테이블 패턴을 사용하여 Azure Well-Architected 프레임워크 핵심 요소에서 다루는 목표와 원칙을 해결하는 방법을 평가해야 합니다. 다음 표에서는 이 패턴이 각 핵심 요소의 목표를 지원하는 방법에 대한 지침을 제공합니다.

핵심 요소 이 패턴으로 핵심 목표를 지원하는 방법
안정성은 워크로드가 오류 발생 시 중복성을 구축하고 기능을 유지하여 복원력 및 복구 목표를 충족하는 데 도움이 됩니다. 비동기 인덱스 유지 관리는 임시 인덱스 업데이트 실패가 원본 데이터 쓰기를 차단하는 것을 방지할 수 있습니다. 멱등 처리, 데드 레터 처리, 모니터링, 그리고 정합성 점검은 장애 발생 후 인덱스 일관성을 복원하는 데 도움이 됩니다.

- RE:07 자기 보존
- RE:10 모니터링
성능 효율성은 크기 조정, 데이터 및 코드의 최적화를 통해 워크로드가 수요를 효율적으로 충족 하는 데 도움이 됩니다. 인덱스 테이블은 전체 데이터 검색 없이 기본이 아닌 키 필드에서 빠른 조회를 가능하게 합니다. 분할된 데이터 저장소의 경우 인덱스 테이블은 범위 및 분할 키만으로는 효율적으로 처리할 수 없는 쿼리 순서 지정을 지원하기 위해 해시되지 않은 값으로 항목을 구성할 수 있습니다.

- PE:05 크기 조정 및 분할
- PE:08 데이터 성능

디자인 결정과 마찬가지로 이 패턴이 기둥 내에서 장단점이 발생하는 경우 다른 기둥의 목표에 반하는 것으로 간주합니다.

Example

영화에 대한 정보를 저장하는 애플리케이션을 고려합니다. 카탈로그가 크고 읽기 요청이 많으며, 각 영화에는 하나의 기본 장르가 있고, 배우 관련 쿼리가 빈번하며, 출연진 데이터는 자주 변경되지 않고, 짧은 인덱스 지연은 허용된다고 가정합니다. Table Storage는 각 엔터티를 명명된 속성의 구조화된 집합으로 저장합니다. 모든 엔터티에는 , a PartitionKeyRowKey및 타임스탬프가 포함되며 동일한 테이블의 엔터티는 서로 다른 속성 집합을 가질 수 있습니다.

Table Storage는 다음과 같은 복합 기본 키를 PartitionKeyRowKey사용합니다. 값은 PartitionKey 엔터티가 저장되는 파티션을 결정합니다. 하나의 파티션 내에서 RowKey 값은 엔터티를 고유하게 식별합니다. Table Storage는 두 키를 지정하거나 한 파티션 내에서 연속된 행 키 값 범위를 가져오는 쿼리에 최적화되어 있습니다.

Tip

Table Storage는 엔터티 그룹 트랜잭션을 통해 동일한 테이블 및 파티션의 엔터티에 대한 트랜잭션 업데이트를 지원합니다. 트랜잭션은 팩트 테이블과 별도의 인덱스 테이블에 걸칠 수 없습니다. 팩트 엔터티 및 인덱스 엔터티를 원자성으로 업데이트하려면 동일한 PartitionKey테이블에 저장합니다. 엔터티 그룹 트랜잭션은 일괄 처리당 100개의 엔터티로 제한되며 최대 페이로드는 4MiB입니다.

이 예제에서는 인코딩된 장르 식별자를 파티션 키로 사용하고 안정적이고 고유한 동영상 식별자를 행 키로 사용하여 각 장르에 대한 파티션이 있는 Azure 테이블을 만듭니다. 장르 및 영화 이름을 속성으로 저장합니다. 다음 그림에서는 식별자 대신 읽을 수 있는 이름을 사용하여 예제를 더 쉽게 따를 수 있도록 합니다.

Azure 테이블 파티션의 영화 데이터 다이어그램. 읽을 수 있는 장르 및 영화 이름은 인코딩된 장르 파티션 키와 고유한 영화 행 키를 나타냅니다.

이 방법은 애플리케이션이 주연 배우를 기준으로 영화도 조회해야 하는 경우에는 덜 효과적입니다. 이 경우 인덱스 테이블 역할을 하는 별도의 Azure 테이블을 만듭니다. 인코딩된 안정적인 행위자 식별자를 파티션 키로 사용하고 영화 식별자를 행 키로 사용합니다. 배우 및 영화 이름을 속성으로 저장합니다. 다음 그림에서는 식별자 대신 읽을 수 있는 이름을 사용합니다. 영화에 두 명 이상의 배우가 출연하면 같은 영화가 여러 파티션에 나타납니다.

다음 그림에서는 행위자 인덱스 테이블을 보여줍니다.

영화 데이터를 복제하고, 행위자를 파티션 키로 사용하고, 영화를 행 키로 사용하는 인덱스 테이블 역할을 하는 행위자 파티션의 다이어그램.

디자인 둘러보기

영화 테이블은 장르를 파티션 키로 사용합니다. 즉, 장르별로 필터링하는 쿼리는 연속 행 키 범위에 대한 파티션 검색으로 효율적으로 실행됩니다. 그러나 Table Storage는 PartitionKey 및 RowKey에 대해서만 단일 클러스터형 인덱스를 지원합니다. 보조 인덱스가 없습니다. "특정 행위자가 출연하는 모든 영화 찾기"와 같은 쿼리를 사용하려면 모든 장르 파티션에서 전체 테이블 검색이 필요하며 대규모로 비용이 많이 듭니다.

행위자 인덱스 테이블은 액세스 패턴을 반전하여 이 제한을 해결합니다. 각 행위자 식별자는 파티션 키가 되고 각 영화 식별자는 행 키가 되므로 행위자 기반 쿼리는 효율적인 파티션 조회로 확인됩니다. 각 파티션에는 행위자의 동영상이 하나만 있으므로 쿼리는 관련 없는 데이터를 검사하지 않고 연속된 엔터티 범위를 반환합니다.

영화 및 행위자 항목은 별도의 테이블과 파티션 키를 사용하므로 이러한 항목은 엔터티 그룹 트랜잭션을 공유할 수 없습니다. 내구성 있는 비동기 변경 캡처 메커니즘을 통해 액터 인덱스를 유지하고, 애플리케이션이 일시적인 쿼리 최신성 저하를 허용하도록 설계합니다.

인덱스 테이블은 부분 비정규화를 적용합니다. 각 항목은 일반적으로 액세스되는 필드(예: 다른 행위자의 이름)를 복제하므로 단일 조회를 통해 인덱스 테이블에서만 가장 자주 쿼리에 응답할 수 있습니다. 액세스 빈도가 낮은 필드의 경우 항목에는 원래 영화 테이블의 장르 파티션 키가 포함되며, 대상 지점 쿼리에서 전체 레코드에 대한 장르 파티션으로 설정할 수 있습니다. 이 디자인은 스토리지 비용 및 유지 관리 오버헤드와 쿼리 속도의 균형을 유지합니다.

다음 단계

다음 패턴은 이 패턴을 구현할 때도 관련이 있을 수 있습니다.

  • 분할 패턴입니다. 인덱스 테이블 패턴은 샤드를 사용해 분할한 데이터와 함께 자주 사용됩니다. 분할 패턴은 데이터 저장소를 분할된 데이터베이스 집합으로 나누는 방법을 설명합니다.

  • 구체화된 뷰 패턴입니다. 데이터를 요약하는 쿼리를 지원하기 위해 데이터를 인덱싱하는 대신 데이터의 구체화된 뷰를 만드는 것이 더 적합할 수 있습니다. 이 패턴은 효율적인 요약 쿼리를 지원하기 위해 데이터에 대해 미리 채워진 뷰를 생성하는 방법을 설명합니다.

  • 트랜잭션 아웃박스 패턴입니다. 트랜잭션 아웃박스 패턴을 사용하여 한 트랜잭션에서 원본 데이터 및 인덱스 항목을 업데이트할 수 없는 경우 비동기 인덱스 유지 관리에 대한 변경 내용을 안정적으로 게시할 수 있습니다.