기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
Neptune의 트랜잭션 격리 수준
Amazon Neptune은 읽기 전용 쿼리와 변형 쿼리에 대해 서로 다른 트랜잭션 격리 수준을 구현합니다. SPARQL 및 Gremlin 쿼리는 다음 기준에 따라 읽기 전용 또는 돌연변이로 분류됩니다.
-
에서는 읽기 쿼리(
SELECT
1SPARQL.1 쿼리 언어사양에 정의된 DESCRIBE
,ASK
CONSTRUCT
, 및 )와 돌연변이 쿼리(INSERT
SPARQL1.1 업데이트사양에 DELETE
정의된 및 )SPARQL가 명확하게 구분됩니다.Neptune에서는 함께 제출된 여러 변형 쿼리(예:
POST
메시지에서 세미콜론으로 구분)를 단일 트랜잭션으로 처리합니다. 원자성 단위로 성공 또는 실패하도록 보장되며 실패할 경우 부분 변경 사항이 롤백됩니다. 하지만 Gremlin에서는 데이터를 조작하는
addE()
,addV()
,property()
또는drop()
와 같이 쿼리 경로 단계를 포함하고 있는지 여부에 따라 Neptune이 쿼리를 읽기 전용 쿼리 또는 변형 쿼리로 분류합니다. 쿼리에 이러한 경로 단계가 포함된 경우에는 변형 쿼리로 분류되어 실행됩니다.
Gremlin에서는 지속 세션을 사용하는 것도 가능합니다. 자세한 내용은 Gremlin 스크립트 기반 세션 단원을 참조하십시오. 이러한 세션에서는 읽기 전용 쿼리를 포함한 모든 쿼리가 라이터 엔드포인트의 변형 쿼리와 동일한 격리 상태에서 실행됩니다.
에서 볼트 읽기-쓰기 세션을 사용하면 읽기 전용 쿼리를 포함한 openCypher모든 쿼리가 라이터 엔드포인트에서 돌연변이 쿼리와 동일한 격리 하에 실행됩니다.
Neptune의 읽기 전용 쿼리 격리
Neptune은 스냅샷 격리 시맨틱에 따라 읽기 전용 쿼리를 평가합니다. 이는 읽기 전용 쿼리가 쿼리 평가가 시작될 때 가져온 데이터베이스의 일관된 스냅샷에서 논리적으로 작동한다는 것을 의미합니다. 따라서 Neptune은 다음 현상 중 어떤 것도 발생하지 않도록 보장합니다.
Dirty reads
– Neptune의 읽기 전용 쿼리에는 동시 트랜잭션에서 커밋되지 않은 데이터가 절대 나타나지 않습니다.Non-repeatable reads
– 동일한 데이터에 대한 읽기를 한 번 이상 수행한 읽기 전용 트랜잭션은 항상 동일한 값을 돌려받게 됩니다.Phantom reads
– 읽기 전용 트랜잭션은 트랜잭션이 시작된 이후에 추가된 데이터를 절대로 읽지 않습니다.
다중 버전 동시성 제어(MVCC)를 사용하여 스냅샷 격리를 달성하므로 읽기 전용 쿼리는 데이터를 잠글 필요가 없으므로 돌연변이 쿼리를 차단하지 않습니다.
읽기 전용 복제본은 읽기 전용 쿼리만 수락하므로 읽기 전용 복제본에 대한 모든 쿼리는 SNAPSHOT
격리 시맨틱에 따라 실행됩니다.
읽기 전용 복제본을 쿼리할 때 유일하게 추가로 고려해야 할 사항은 라이터와 읽기 전용 복제본 간에 약간의 복제 지연이 발생할 수 있다는 것입니다. 이는 라이터에서 수행된 업데이트가 읽기 작업을 수행하려는 읽기 전용 복제본에 전파되는 데 약간 시간이 걸릴 수 있다는 것을 뜻합니다. 실제 복제 시간은 기본 인스턴스에 대한 쓰기 로드에 따라 달라집니다. Neptune 아키텍처는 지연 시간이 짧은 복제를 지원하며 복제 지연은 Amazon CloudWatch 지표에서 계측됩니다.
하지만 읽기 쿼리는 여전히 SNAPSHOT
격리 수준으로 인해 최신본이 아니더라도 데이터베이스의 상태가 항상 일관되게 나타납니다.
쿼리가 이전 업데이트의 결과를 관찰하도록 강력하게 보장해야 하는 경우에는 쿼리를 읽기 전용 복제본이 아니라 라이터 엔드포인트 자체로 전송합니다.
Neptune의 변형 쿼리 격리
변형 쿼리의 일환으로 수행된 읽기는 더티 읽기의 가능성을 배제하는 READ COMMITTED
트랜잭션 격리에 따라 실행됩니다. READ COMMITTED
트랜잭션 격리에서 제공되는 일반적인 보장 수준을 넘어서면 Neptune은 NON-REPEATABLE
또는 PHANTOM
읽기가 이루어지지 않도록 강력한 보장을 제공합니다.
이렇게 강력한 보장은 데이터를 읽을 때 레코드와 레코드의 범위를 잠금으로써 달성됩니다. 이렇게 하면 동시 트랜잭션이 읽기가 수행된 이후에 인덱스 범위에서 삽입 또는 삭제를 수행하지 못하도록 막을 수 있고, 따라서 반복 가능한 읽기가 보장됩니다.
참고
그러나 동시 변형 트랜잭션 Tx2
은 변형 트랜잭션 Tx1
가 시작된 이후에 시작될 수 있었으며, Tx1
이 읽어올 데이터를 잠그기 전에 변경을 커밋할 수 있었습니다. 이 경우, 마치 Tx1
이 시작되기 전에 Tx2
이 완료된 것처럼 Tx1
가 Tx2
의 변경을 확인하게 됩니다. 이는 커밋된 변경에만 적용되기 때문에 dirty
read
은 절대로 일어날 수 없습니다.
Neptune이 변형 쿼리에서 사용하는 잠금 메커니즘을 이해하려면 먼저 Neptune 그래프 데이터 모델 및 인덱싱 전략의 세부 사항을 이해해야 합니다. Neptune은 3개의 인덱스(SPOG
, POGS
, GPSO
)를 사용하여 데이터를 관리합니다.
READ COMMITTED
트랜잭션 수준에서 반복 가능한 읽기를 달성하기 위해 Neptune은 사용 중인 인덱스에서 범위 잠금을 적용하고 있습니다. 예를 들어 변형 쿼리가 person1
라는 버텍스의 모든 속성과 발신 엣지를 읽고 있는 경우, 노드는 데이터를 읽기 전에 SPOG
인덱스에서 접두사 S=person1
로 정의된 전체 범위를 잠급니다.
다른 인덱스를 사용할 때도 같은 메커니즘이 적용됩니다. 예를 들어 변형 트랜잭션이 POGS
인덱스를 사용해 주어진 엣지 레이블에 대한 모든 소스-대상 버텍스 페어를 조회하면 P
위치에 있는 엣지 레이블의 범위가 잠금 상태가 됩니다. 읽기 전용 쿼리였든 변형 쿼리였든 관계 없이 모든 동시 트랜잭션은 잠긴 범위 내에서 여전히 읽기를 수행할 수 있습니다. 그러나 잠긴 접두사 범위에서 새 레코드를 삽입 또는 삭제하는 모든 변형 쿼리에서는 포괄적인 잠금을 통해 작업이 금지됩니다.
즉, 인덱스의 범위를 변형 쿼리에서 읽었을 때 읽기 트랜잭션이 끝날 때까지 어떤 동시 트랜잭션도 이 범위를 수정하지 못하도록 강력하게 보장할 수 있습니다. 이렇게 하면 non-repeatable
reads
이 발생하지 않도록 보장할 수 있습니다.
잠금-대기 제한 시간을 이용한 충돌 해결
첫 번째 트랜잭션이 잠금 상태가 된 범위에서 두 번째 트랜잭션이 레코드를 수정하려고 시도하면 Neptune은 이러한 충돌을 즉시 감지하고 두 번째 트랜잭션을 차단합니다.
종속성 교착 상태가 감지되지 않으면 Neptune은 잠금-대기 제한 시간 메커니즘을 자동으로 적용하고, 차단된 트랜잭션은 작업이 완료되어 잠금이 해제될 때까지 잠금 상태를 유지한 채로 최대 60초 동안 대기하게 됩니다.
잠금이 해제되기 전에 잠금-대기 제한 시간이 만료되면 잠긴 트랜잭션이 롤백됩니다.
잠금-대기 제한 시간 내에 잠금이 해제되면 두 번째 트랜잭션의 차단이 해제되어 재시도 없이도 성공적으로 완료될 수 있습니다.
그러나 Neptune이 두 트랜잭션 간에 종속성 교착 상태를 감지한 경우에는 충돌에 대한 자동 조정이 불가능합니다. 이 경우, Neptune은 잠금-대기 제한 시간을 시작하지 않고 두 트랜잭션 중 하나를 즉시 취소하고 롤백합니다. Neptune은 삽입 또는 삭제된 레코드가 가장 적은 트랜잭션을 롤백하기 위해 최선을 다합니다.
범위 잠금 및 허위 충돌
Neptune은 GAP 잠금을 사용하여 범위 잠금을 적용합니다. GAP 잠금은 인덱스 레코드 사이의 간격을 잠그거나 첫 번째 인덱스 레코드 앞 또는 마지막 인덱스 레코드 뒤의 간격을 잠그는 것입니다.
Neptune은 소위 딕셔너리 표를 사용하여 숫자 ID 값을 특정 문자열 리터럴과 연결합니다. 다음은 이러한 Neptune 딕셔너리 표의 샘플 상태입니다.
String | ID |
---|---|
type |
1 |
default_graph |
2 |
person_3 |
3 |
person_1 |
5 |
알고 있습니다 |
6 |
person_2 |
7 |
age |
8 |
edge_1 |
9 |
lives_in |
10 |
뉴욕 |
11 |
Person |
12 |
Place |
13 |
엣지_2 |
14 |
위의 문자열은 속성 그래프 모델에 속하지만 개념은 모든 RDF 그래프 모델에도 동일하게 적용됩니다.
SPOG (Subject-Predicate-Object_Graph) 인덱스의 해당 상태는 왼쪽 아래에 나와 있습니다. 오른쪽에는 인덱스 데이터가 의미하는 바를 이해하는 데 도움이 되는 해당 문자열이 표시됩니다.
S(ID) | P(ID) | O(ID) | G(ID) | S(문자열) | P(문자열) | O(문자열) | G(문자열) | |
---|---|---|---|---|---|---|---|---|
3 |
1 |
12 |
2 |
person_3 |
type |
Person |
default_graph |
|
5 |
1 |
12 |
2 |
person_1 |
type |
Person |
default_graph |
|
5 |
6 |
3 |
9 |
person_1 |
알고 있습니다 |
person_3 |
edge_1 |
|
5 |
8 |
40 |
2 |
person_1 |
age |
40 |
default_graph |
|
5 |
10 |
11 |
14 |
person_1 |
lives_in |
뉴욕 |
엣지_2 |
|
7 |
1 |
12 |
2 |
person_2 |
type |
Person |
default_graph |
|
11 |
1 |
13 |
2 |
뉴욕 |
type |
Place |
default_graph |
이제 돌연변이 쿼리가 라는 꼭짓점의 모든 속성과 발신 엣지를 읽는 경우 person_1
노드는 데이터를 읽기 전에 SPOG 인덱스S=person_1
의 접두사로 정의된 전체 범위를 잠급니다. 범위 잠금은 일치하는 모든 레코드와 일치하지 않는 첫 번째 레코드에 GAP 잠금을 적용합니다. 일치하는 레코드는 잠기고 일치하지 않는 레코드는 잠기지 않습니다. Neptune은 다음과 같이 GAP 잠금을 적용합니다.
5 1 12 2
(GAP 1)5 6 3 9
(GAP 2)5 8 40 2
(GAP 3)5 10 11 14
(GAP 4)7 1 12 2
(GAP 5)
이렇게 하면 다음 레코드가 잠깁니다.
5 1 12 2
5 6 3 9
5 8 40 2
5 10 11 14
이 상태에서는 다음 작업이 정상적으로 차단됩니다.
S=person_1
에 대한 새 속성 또는 엣지 삽입.type
이나 새 엣지와 다른 새 속성은 모두 잠겨 있는 GAP 2, GAP 3, GAP 4 또는 GAP 5 중 하나로 설정되어야 합니다.기존 레코드 모두 삭제.
동시에 몇 개의 동시 작업이 잘못 차단되어 허위 충돌이 발생할 수 있습니다.
S=person_3
에 대한 모든 속성이나 엣지 삽입은 GAP 1로 설정되어야 하므로 차단됩니다.3에서 5 사이의 ID가 할당된 새 버텍스 삽입은 GAP 1로 설정되어야 하므로 차단됩니다.
5에서 7 사이의 ID가 할당된 새 버텍스 삽입은 GAP 5로 설정되어야 하므로 차단됩니다.
GAP 잠금은 특정 조건자 하나에 대한 간격을 잠글 만큼 정확하지 않습니다(예: 조건자 S=5
에 대한 GAP 5 잠금).
범위 잠금은 읽기가 발생하는 인덱스에만 적용됩니다. 위의 경우 레코드는 POGS 또는 가 아닌 SPOG 인덱스에서만 잠깁니다GPSO. 쿼리에 대한 읽기는 액세스 패턴에 따라 모든 인덱스에서 수행될 수 있으며, (Sparql의 경우 , Gremlin의 경우 explain
APIs )를 사용하여 나열할 수 있습니다.
참고
또한 기본 인덱스의 안전한 동시 업데이트를 위해 GAP 잠금을 사용할 수 있으며, 이로 인해 잘못된 충돌이 발생할 수도 있습니다. 이러한 GAP 잠금은 트랜잭션에서 수행되는 격리 수준 또는 읽기 작업과는 독립적으로 적용됩니다.
GAP 잠금으로 인해 동시 트랜잭션이 충돌하는 경우뿐만 아니라 어떤 종류의 실패 후 트랜잭션이 재시도되는 경우에도 허위 충돌이 발생할 수 있습니다. 실패로 인해 트리거된 롤백이 아직 진행 중이고 앞서 트랜잭션에 적용된 잠금이 아직 완전히 해제되지 않은 경우 재시도 시 허위 충돌이 발생하여 실패합니다.
부하가 높으면 일반적으로 쓰기 쿼리의 3~4%가 허위 충돌로 인해 실패할 수 있습니다. 외부 클라이언트의 경우 이러한 허위 충돌은 예측하기 어려우므로 재시도를 통해 처리해야 합니다.