G·Graph Daily Lab 전체 140회
추가 19 / 40 · 보충 · 약 12분

모두 되거나 하나도 안 되게, 트랜잭션

여러 쓰기를 한 단위로 묶는 트랜잭션과 ACID 성질, 그리고 동시에 고칠 때 생길 수 있는 문제를 봅니다.

그래프 DB 엔진과 선택

배운 뒤 돌아올 회차

DAY 49에서 트랜잭션 순회(OLTP) 워크로드를 봤다면, 그 ‘트랜잭션’이 무엇을 보장하고 무엇을 보장하지 않는지 확인하러 옵니다.

시작하면 직접 풀기와 퀴즈 기록이 저장됩니다. 본과정 100회 진도와는 별도입니다.

이번에 더 배울 것

트랜잭션은 여러 읽기·쓰기를 하나의 단위로 묶은 것이고, ACID는 그 보장을 네 글자로 줄인 말입니다. 원자성(Atomicity)은 전부 반영되거나 전혀 반영되지 않음, 일관성(Consistency)은 제약 같은 규칙이 지켜짐, 격리성(Isolation)은 동시에 실행되는 트랜잭션이 서로의 중간 상태를 보지 않음, 지속성(Durability)은 커밋된 결과가 장애 뒤에도 남음을 뜻합니다. Neo4j 문서는 트랜잭션이 ACID를 따른다고 밝힙니다.

민수가 연구실을 옮기는 일은 기존 WORKS_AT에 to를 적고 새 WORKS_AT을 만드는 두 쓰기입니다. 이 둘을 한 트랜잭션에 넣어야 중간에 실패해도 ‘두 곳 소속’이나 ‘소속 없음’ 상태가 남지 않습니다. 같은 트랜잭션의 한 쓰기가 유일성 제약을 어기면, 이미 실행한 다른 쓰기까지 함께 롤백됩니다.

격리에는 수준이 있습니다. Neo4j의 기본은 read committed로, 한 트랜잭션이 읽은 데이터에 다른 트랜잭션이 쓰는 것을 막지 않습니다. 그래서 값을 읽어 앱에서 계산한 뒤 따로 쓰면 두 요청이 서로의 갱신을 덮어쓰는 갱신 손실이 생길 수 있습니다. SET p.views = p.views + 1처럼 한 문장 안에서 읽고 쓰면 쓰기 잠금이 자동으로 잡힙니다. 교착 상태가 감지되면 Neo4j는 한 트랜잭션을 일시적 오류로 끝내므로, 앱은 재시도를 준비해야 합니다.

작은 예제로 따라가기

1

이동 트랜잭션: 기존 WORKS_AT에 to=2024를 쓰고 새 WORKS_AT을 만듭니다. 둘 다 커밋되거나 둘 다 취소됩니다.

2

views가 10일 때 두 요청이 각각 10을 읽고 앱에서 11을 계산해 쓰면, 최종값이 12가 아니라 11이 될 수 있습니다.

3

MATCH (p:Paper {doi:$doi}) SET p.views = p.views + 1처럼 한 문장으로 쓰면 쓰기 잠금 덕분에 두 요청이 차례로 반영되어 12가 됩니다.

COMPARE & EXPLAIN

동시에 두 명이 고치면

views가 10인 논문에 두 요청이 동시에 +1을 할 때, 그리고 이동 트랜잭션이 중간에 실패할 때 최종 상태를 먼저 예상하세요.

views = 11이 될 수 있음

read committed에서는 두 요청이 모두 10을 읽고 11을 써서 갱신 한 번이 사라질 수 있습니다.

트랜잭션은 여러 쓰기를 원자적으로 묶지만, 기본 격리 수준에서는 읽고-계산하고-쓰는 갱신을 한 문장이나 잠금으로 보호해야 합니다.

학습을 시작하면 새 문제를 직접 풀고 확인 퀴즈를 마친 뒤 선택한 본과정 회차로 돌아갑니다.