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
- INSERT for inserting rows
- UPDATE for updating rows
- DELETE for deleting rows
- Compatibility for differences from SQLite
더 알아보기 (Learn more)
- INSERT - 행 삽입
- UPDATE - 행 갱신
- DELETE - 행 삭제
- Compatibility - SQLite와의 차이점