본문 바로가기
WIKI 기술 지식 베이스

Transactions

원문 보기 위키 갱신

여러 SQL 문장을 하나의 원자적(atomic) 작업 단위로 묶어 주는 트랜잭션이에요. 트랜잭션 안의 모든 문장이 성공해 커밋되거나, 아니면 전체가 롤백되어 아무 변경도 남지 않거나 둘 중 하나예요.

출처: 문서

본문

트랜잭션은 여러 SQL 문장을 하나의 원자적 작업 단위로 묶어요. 트랜잭션 안의 모든 문장이 성공해서 커밋되거나, 트랜잭션 전체가 롤백되어 아무 변경도 적용되지 않거나 둘 중 하나예요. Turso는 BEGIN, COMMIT, ROLLBACK으로 명시적 트랜잭션을 지원하고, 단독으로 실행되는 각 문장도 암시적 트랜잭션(autocommit 모드)으로 처리해요.

Syntax

BEGIN [DEFERRED | IMMEDIATE | EXCLUSIVE] [TRANSACTION];

COMMIT [TRANSACTION];
END [TRANSACTION];

ROLLBACK [TRANSACTION] [TO [SAVEPOINT] savepoint-name];

SAVEPOINT savepoint-name;
RELEASE [SAVEPOINT] savepoint-name;

BEGIN

BEGIN 문은 새 명시적 트랜잭션을 시작해요. 트랜잭션이 열려 있는 동안에는 COMMIT이나 ROLLBACK을 실행하기 전까지 이어지는 모든 문장이 그 트랜잭션의 일부가 돼요.

Transaction Types

Type Description
DEFERRED 기본값이에요. 첫 읽기나 쓰기 연산이 일어날 때까지 잠금을 걸지 않아요. 읽기 연산은 공유 잠금을, 쓰기 연산은 쓰기 잠금을 획득해요.
IMMEDIATE BEGIN을 실행하는 즉시 쓰기 잠금을 획득해요. 트랜잭션이 끝날 때까지 다른 연결은 읽을 수는 있지만 쓸 수는 없어요.
EXCLUSIVE Turso에서는 IMMEDIATE와 같은 동작이에요. Turso는 항상 WAL 모드를 사용하고, WAL 모드에서는 EXCLUSIVE와 IMMEDIATE가 동일해요.

타입을 지정하지 않으면 트랜잭션은 DEFERRED가 기본이에요.

-- 기본값인 지연(deferred) 트랜잭션 시작
BEGIN;

-- 즉시(immediate) 트랜잭션 시작
BEGIN IMMEDIATE;

-- TRANSACTION 키워드는 선택 사항이에요
BEGIN IMMEDIATE TRANSACTION;

COMMIT / END

COMMIT 문(또는 그 별칭인 END)은 트랜잭션을 마무리하고 모든 변경을 데이터베이스에 기록해요. 커밋이 끝나면 변경은 영구적이 되고 다른 연결에서도 보여요.

BEGIN;
INSERT INTO accounts (name, balance) VALUES ('Alice', 1000);
INSERT INTO accounts (name, balance) VALUES ('Bob', 500);
COMMIT;

COMMIT과 END는 서로 바꿔 쓸 수 있어요.

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE name = 'Alice';
END;

ROLLBACK

ROLLBACK 문은 현재 트랜잭션을 중단하고 BEGIN 이후에 한 모든 변경을 되돌려요. 데이터베이스는 트랜잭션이 시작되기 전 상태 그대로 남아요.

BEGIN;
DELETE FROM accounts WHERE balance < 0;
-- 마음이 바뀌었으니 전부 되돌려요
ROLLBACK;

Implicit Transactions

명시적 트랜잭션이 활성화되어 있지 않으면 Turso는 각 문장을 개별적인 암시적 트랜잭션으로 감싸요. 이것을 autocommit 모드라고 불러요. 각 문장은 성공적으로 실행되면 자동으로 커밋되고, 실패하면 자동으로 롤백돼요.

-- 각 문장은 자기만의 암시적 트랜잭션으로 실행돼요
INSERT INTO logs (msg) VALUES ('first');
INSERT INTO logs (msg) VALUES ('second');
-- 두 번째 INSERT가 실패해도 첫 번째 INSERT는 커밋된 상태로 남아요

Transaction Lifecycle

전형적인 트랜잭션 생명주기예요.

-- 1. 트랜잭션을 시작해요
BEGIN;

-- 2. 작업을 수행해요
UPDATE inventory SET quantity = quantity - 1 WHERE product_id = 42;
INSERT INTO orders (product_id, customer_id) VALUES (42, 7);

-- 3a. 모두 성공했다면 커밋해요
COMMIT;

-- 또는 3b. 뭔가 잘못됐다면 롤백해요
ROLLBACK;

각 연결은 한 번에 최대 하나의 활성 최상위 트랜잭션을 가질 수 있어요. 트랜잭션 안에서 더 세밀한 롤백이 필요하면 savepoint를 사용하세요.

Savepoints

savepoint는 트랜잭션 안에 이름을 붙인 표식이에요. 트랜잭션 전체를 중단하지 않고도 작업의 일부만 롤백할 수 있게 해 주고, 중첩할 수도 있어요.

Statement Description
SAVEPOINT name 주어진 이름으로 새 savepoint를 만들어요.
RELEASE [SAVEPOINT] name savepoint(그리고 그 이후에 만든 savepoint들)를 제거하고, 그 변경들은 바깥 트랜잭션의 일부로 유지돼요.
ROLLBACK [TRANSACTION] TO [SAVEPOINT] name savepoint 이후의 모든 변경을 되돌리지만, savepoint와 트랜잭션은 열린 상태로 유지돼요.
BEGIN;
INSERT INTO accounts (name, balance) VALUES ('Alice', 1000);

SAVEPOINT before_bonus;
UPDATE accounts SET balance = balance + 500 WHERE name = 'Alice';

-- 보너스만 되돌리고 INSERT는 유지해요
ROLLBACK TO before_bonus;

-- savepoint를 버리고 나머지는 유지해요
RELEASE before_bonus;
COMMIT;
같은 연결에서 쓰기 문장이 아직 진행 중일 때 `SAVEPOINT`, `RELEASE`, `ROLLBACK TO`는 `SQLITE_BUSY`를 돌려줄 수 있어요.

BEGIN CONCURRENT

**Turso 확장**: BEGIN CONCURRENT는 표준 SQLite에는 없는 Turso 전용 확장이에요. MVCC 모드가 필요해요.

BEGIN CONCURRENT 문은 여러 연결이 동시에 쓸 수 있게 허용하는 낙관적 동시 쓰기 트랜잭션을 시작해요.

Syntax

BEGIN CONCURRENT [TRANSACTION];

Requirements

BEGIN CONCURRENT는 MVCC 모드가 필요해요. 트랜잭션을 열기 전에 저널 모드를 설정해서 활성화하세요.

PRAGMA journal_mode = mvcc;

How It Works

동시(concurrent) 트랜잭션은 BEGIN CONCURRENT 시점에 찍힌 데이터베이스 스냅샷 위에서 동작해요. 각 연결은 자기 스냅샷에서 읽고 독립적으로 써요.

COMMIT을 실행하면 Turso는 스냅샷 이후에 다른 트랜잭션이 같은 행을 수정했는지 검사해요.

  • 충돌이 없으면 트랜잭션이 성공적으로 커밋돼요.
  • 쓰기-쓰기(write-write) 충돌이 감지되면 COMMIT이 실패하고, 트랜잭션을 롤백한 뒤 다시 시도해야 해요.

이것이 **스냅샷 격리(snapshot isolation)**예요. 각 트랜잭션은 시작 시점 기준의 일관된 데이터베이스 뷰를 보면서 다른 쓰기 작업을 막지 않아요.

Example

-- 연결 1
PRAGMA journal_mode = mvcc;
BEGIN CONCURRENT;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

-- 연결 2 (동시에 실행)
PRAGMA journal_mode = mvcc;
BEGIN CONCURRENT;
UPDATE accounts SET balance = balance + 50 WHERE id = 2;
COMMIT;
-- 서로 다른 행을 수정했기 때문에 둘 다 성공해요

두 연결이 같은 행을 수정하면 두 번째 COMMIT이 실패해요.

-- 연결 1
BEGIN CONCURRENT;
UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT;  -- 성공

-- 연결 2 (연결 1이 커밋하기 전에 시작)
BEGIN CONCURRENT;
UPDATE accounts SET balance = 800 WHERE id = 1;
COMMIT;  -- 실패: 쓰기-쓰기 충돌이 감지됨
ROLLBACK;
-- BEGIN CONCURRENT부터 트랜잭션을 다시 시도해요

See Also

더 알아보기 (Learn more)