트랜잭션 및 낙관적 동시성 제어

적용 대상: NoSQL

데이터베이스 트랜잭션은 데이터에 대한 동시 변경을 처리하기 위한 안전하고 예측 가능한 프로그래밍 모델을 제공합니다. SQL Server와 같은 기존 관계형 데이터베이스를 통해 저장 프로시저 및/또는 트리거를 사용하여 비즈니스 논리를 작성하고 데이터베이스 엔진 내에서 직접 실행하도록 서버에 보낼 수 있습니다. 기존의 관계형 데이터베이스를 사용하면 두 개의 서로 다른 프로그래밍 언어(JavaScript, Python, C#, Java 등과 같은 (비트랜잭션) 애플리케이션 프로그래밍 언어 및 데이터베이스에서 고유하게 실행되는 T-SQL(트랜잭션 프로그래밍 언어))를 처리해야 합니다.

Azure Cosmos DB의 데이터베이스 엔진은 스냅샷 격리를 사용한 전체 ACID(원자성, 일관성, 격리, 내구성) 준수 트랜잭션을 지원합니다. 컨테이너 논리 파티션의 범위 내 모든 데이터베이스 작업은 파티션의 복제본에 의해 호스트되는 데이터베이스 엔진 내에서 트랜잭션 방식으로 실행됩니다. 이러한 작업은 쓰기(논리 파티션 내에서 하나 이상의 항목을 업데이트) 및 읽기 작업을 모두 포함합니다. 다음 표에서는 다양한 작업 및 트랜잭션 형식을 설명합니다.

연산 작업 유형 단일 또는 다중 항목 트랜잭션
삽입(사전/사후 트리거 없음) 쓰기 단일 항목 트랜잭션
삽입(사전/사후 트리거 있음) 쓰기 및 읽기 다중 항목 트랜잭션
바꾸기(사전/사후 트리거 없음) 쓰기 단일 항목 트랜잭션
바꾸기(사전/사후 트리거 있음) 쓰기 및 읽기 다중 항목 트랜잭션
Upsert(사전/사후 트리거 없음) 쓰기 단일 항목 트랜잭션
Upsert(사전/사후 트리거 있음) 쓰기 및 읽기 다중 항목 트랜잭션
삭제(사전/사후 트리거 없음) 쓰기 단일 항목 트랜잭션
삭제(사전/사후 트리거 있음) 쓰기 및 읽기 다중 항목 트랜잭션
저장 프로시저 실행 쓰기 및 읽기 다중 항목 트랜잭션
시스템이 병합 프로시저 실행을 시작함 쓰기 다중 항목 트랜잭션
시스템이 항목의 만료(TTL)를 기준으로 항목을 삭제 실행을 시작함 쓰기 다중 항목 트랜잭션
읽음 읽음 단일 항목 트랜잭션
변경 피드 읽음 다중 항목 트랜잭션
페이지를 매긴 읽기 읽음 다중 항목 트랜잭션
페이지를 매긴 쿼리 읽음 다중 항목 트랜잭션
페이지를 매긴 쿼리의 일부로 UDF 실행 읽음 다중 항목 트랜잭션

다중 항목 트랜잭션

Azure Cosmos DB를 사용하면 저장 프로시저, 사전/사후 트리거, UDF(사용자 정의 함수) 및 병합 프로시저를 JavaScript에 쓸 수 있습니다. Azure Cosmos DB는 기본적으로 해당 데이터베이스 엔진 내에서 JavaScript 실행을 지원합니다. 저장 프로시저, 사전/사후 트리거, UDF(사용자 정의 함수) 및 병합 프로시저를 컨테이너에서 등록하고 나중에 Azure Cosmos DB 데이터베이스 엔진 내에 트랜잭션 방식으로 실행할 수 있습니다. JavaScript에서 애플리케이션 논리를 작성하면 제어 흐름, 변수 범위 지정, 할당 및 예외 처리 기본 형식의 통합을 JavaScript 언어로 직접 데이터베이스 트랜잭션 내에서 자연스럽게 표현할 수 있습니다.

JavaScript 기반 저장 프로시저, 트리거, UDF 및 병합 프로시저는 논리 파티션 내 모든 항목에서 스냅샷 격리를 사용하여 앰비언트 ACID 트랜잭션 내에서 래핑됩니다. 실행 도중에 JavaScript에서 예외가 발생하면 전체 트랜잭션이 중단되고 롤백됩니다. 결과로 생성된 프로그래밍 모델은 단순하지만 강력합니다. JavaScript 개발자는 익숙한 언어 구문과 라이브러리 기본 형식을 사용하면서 동시에 내구성 있는 프로그래밍 모델을 얻게 됩니다.

데이터베이스 엔진 내에서 직접 JavaScript를 실행할 수 있어 컨테이너의 항목에 대한 데이터베이스 작업의 트랜잭션 실행 및 성능을 제공합니다. 또한 Azure Cosmos DB 데이터베이스 엔진은 기본적으로 JSON 및 JavaScript를 지원하므로 애플리케이션과 데이터베이스의 형식 시스템 간 불일치가 제거됩니다.

낙관적 동시성 제어

낙관적 동시성 제어를 통해 업데이트 손실 및 삭제를 방지할 수 있습니다. 충돌하는 동시 작업은 항목을 소유하는 논리 파티션에서 호스팅된 데이터베이스 엔진의 일반 비관적 잠금에 종속됩니다. 두 개의 동시 작업이 논리 파티션 내 항목의 최신 버전을 업데이트하려고 하면 두 작업 중 하나는 성공하고 다른 하나는 실패합니다. 그러나 동일한 항목을 동시에 업데이트하려는 하나 또는 두 개의 작업에서 이전에 항목의 이전 값을 읽은 경우, 데이터베이스는 충돌하는 하나 또는 두 개의 작업의 이전 읽기 값이 실제로 항목의 최신 값인지 알지 못합니다. 다행히 이 상황은 두 작업이 데이터베이스 엔진 내의 트랜잭션 경계로 진입하기 전에 OCC(낙관적 동시성 제어)를 통해 감지할 수 있습니다. OCC는 다른 사용자가 수행한 변경 내용을 실수로 덮어쓰지 않도록 데이터를 보호합니다. 또한 자신의 변경 내용을 실수로 덮어쓰지 않도록 다른 사용자를 보호합니다.

ETag 및 HTTP 헤더를 사용하여 낙관적 동시성 제어 구현

Azure Cosmos DB 컨테이너에 저장된 모든 항목에는 시스템 정의 _etag 속성이 있습니다. _etag의 값이 자동으로 생성되고, 항목이 업데이트될 때마다 서버에서 업데이트됩니다. _etag는 서버가 항목을 조건적으로 업데이트할 수 있는지 여부를 결정할 수 있도록 클라이언트가 제공한 if-match 요청 헤더와 함께 사용할 수 있습니다. if-match 헤더의 값이 서버의 _etag 값과 일치하면 항목이 업데이트됩니다. if-match 요청 헤더의 값이 최신 상태가 아닌 경우 서버에서는 “HTTP 412 사전 조건 실패” 응답 메시지와 함께 작업을 거부합니다. 그러면 클라이언트가 항목을 다시 페치하여 서버의 현재 버전의 항목을 가져오거나 해당 항목에 대한 자체 _etag 값으로 서버의 항목 버전을 재정의할 수 있습니다. 또한 if-none-match 헤더와 함께 _etag를 사용하여 리소스를 다시 페치해야 하는지를 확인합니다.

항목의 _etag 값은 항목이 업데이트될 때마다 변경됩니다. 바꾸기 항목 작업의 경우 if-match는 요청 옵션의 일부로 명시적으로 표시되어야 합니다. 예제는 GitHub의 샘플 코드를 참조하세요. _etag 값은 저장 프로시저에서 터치한 모든 작성된 항목에 대해 암시적으로 확인됩니다. 충돌이 감지되면 저장 프로시저는 트랜잭션을 롤백하고 예외를 발생시킵니다. 이 메서드를 사용하면 저장 프로시저 내에서 전체 쓰기 또는 쓰기 없음이 자동으로 적용됩니다. 이는 애플리케이션이 업데이트를 다시 적용하고 원래 클라이언트 요청을 다시 시도하기 위한 신호입니다.

낙관적 동시성 제어 및 글로벌 배포

항목의 동시 업데이트는 Azure Cosmos DB의 통신 프로토콜 계층에 의한 OCC에 종속됩니다. 단일 지역 쓰기로 구성된 Azure Cosmos DB 계정의 경우 Azure Cosmos DB는 업데이트(또는 삭제)하는 항목의 클라이언트 쪽 버전이 Azure Cosmos DB 컨테이너의 항목 버전과 동일한지 확인합니다. 그러면 사용자의 쓰기가 실수로 다른 사용자의 쓰기로 덮어 쓰이거나 그 반대의 경우가 발생하지 않도록 방지할 수 있습니다. 다중 사용자 환경에서 낙관적 동시성 제어는 항목의 잘못된 버전이 실수로 삭제 또는 업데이트되지 않도록 보호합니다. 따라서 악명 높은 “업데이트 손실” 또는 “삭제 손실” 문제로부터 항목이 보호됩니다.

다중 지역 쓰기로 구성된 Azure Cosmos DB 계정에서 데이터는 _etag이 로컬 지역의 데이터와 일치하는 경우 보조 지역에 독립적으로 커밋될 수 있습니다. 새 데이터를 보조 지역에서 로컬로 커밋한 후에는 허브 또는 주 지역에 병합합니다. 충돌 해결 정책이 새 데이터를 허브 지역에 병합하는 경우 이 데이터는 전역적으로 새 _etag에 복제됩니다. 충돌 해결 정책이 새 데이터를 거부하는 경우 보조 지역은 원래 데이터 및 _etag로 롤백됩니다.

다음 단계

다음 문서에서 데이터베이스 트랜잭션 및 낙관적 동시성 제어에 대해 자세히 알아보세요.