트랜잭션 기초: 커밋, 롤백, 격리 수준과 자동 커밋
Spring Boot에서 DB를 사용하는 흐름에 맞춰 트랜잭션의 커밋과 롤백부터 살펴봤습니다.
송금과 입금을 하나의 작업으로 봅니다
트랜잭션의 사전적 의미는 거래입니다. A가 B에게 돈을 보내는 상황이라면 A의 잔액을 줄이는 작업과 B의 잔액을 늘리는 작업을 함께 생각해야 합니다.
둘 다 성공했을 때 변경 내용을 확정하는 것이 **커밋(Commit)**입니다. 그런데 A의 잔액만 줄고 B의 잔액을 늘리지 못하면 어떻게 해야 할까요? 그대로 확정하면 안 되므로 진행한 변경을 되돌려야 합니다. 이것이 **롤백(Rollback)**입니다.
트랜잭션의 네 가지 성질: ACID
- 원자성(Atomicity): 트랜잭션에 묶인 작업을 모두 반영하거나 모두 반영하지 않습니다.
- 일관성(Consistency): 트랜잭션 전후에 DB의 무결성 규칙을 만족하는 상태를 유지해야 합니다.
- 격리성(Isolation): 동시에 실행하는 트랜잭션 사이의 영향을 제어합니다. 어느 정도까지 격리할지는 격리 수준에 따라 달라집니다.
- 지속성(Durability): 성공적으로 커밋한 결과는 장애가 발생하더라도 보존되어야 합니다.
당시 공부한 격리 수준은 다음 네 가지였습니다.
READ UNCOMMITTED: 커밋되지 않은 읽기READ COMMITTED: 커밋된 읽기REPEATABLE READ: 반복 가능한 읽기SERIALIZABLE: 직렬화 가능
READ COMMITTED를 일반적으로 많이 쓰는 수준으로 배웠지만 실제 기본값과 보장 범위는 사용하는 DB 제품을 확인해야 합니다.
커넥션과 DB 세션
커넥션을 빌리는 과정과 연결하면 다음과 같습니다.
사용자 요청 → Spring Boot의 커넥션 풀에서 커넥션을 빌림 → 해당 연결의 DB 세션에서 작업 → 트랜잭션을 커밋하거나 롤백
일반적인 직접 연결에서는 물리적인 DB 커넥션 하나에 DB 세션 하나가 대응한다고 정리했습니다. 풀에서 빌릴 때마다 물리 연결이나 세션을 새로 만들지는 않습니다.
처음에는 커밋하지 않은 데이터는 다른 세션에서 볼 수 없다고 생각했습니다. 나중에 롤백할 수도 있는 값을 다른 작업이 읽으면 문제가 생기기 때문입니다. 다만 실제로 읽을 수 있는지는 격리 수준마다 다릅니다.
SQL 표준의 READ UNCOMMITTED는 커밋하지 않은 값을 읽는 더티 리드를 허용합니다. DB별 구현은 다를 수 있습니다. 예를 들어 PostgreSQL 문서는 PostgreSQL의 해당 수준이 READ COMMITTED처럼 동작한다고 설명합니다.
자동 커밋과 수동 커밋
자동 커밋은 문장이 끝날 때 변경을 자동으로 확정하는 방식입니다. 수동 커밋에서는 여러 작업을 묶고 명시적으로 커밋하거나 롤백할 수 있습니다.
수동 커밋을 “커밋 전에는 아무것도 저장되지 않는다”로 받아들이면 조금 어긋납니다. 정확히는 “아직 변경을 최종 확정하지 않았다”는 상태입니다. 자동 커밋 여부와 실제 트랜잭션 시작 시점은 구분해야 합니다. JDBC Connection API는 새 연결의 기본 자동 커밋 동작을 설명합니다. 하지만 Spring이나 풀이 연결 상태를 설정했다면 그 상태까지 항상 같다고 단정할 수는 없습니다.
자동 커밋 설정은 아래처럼 메모했습니다. 특정 DB에서 바로 실행할 SQL은 아니고 설명용 주석도 섞여 있습니다. 실제 문법은 사용하는 DB에 맞춰야 합니다.
set autocommit true //true 는 자동 커밋, false 는 수동 커밋수동 커밋일 때 변경을 확정하거나 취소하는 명령도 함께 적었습니다.
commit // 커밋 수행
rollback // 롤백 수행이때는 자동 커밋이 기본이고 수동 커밋으로 바꾸면 트랜잭션이 시작된다고 이해했습니다. 여러 변경을 묶어 커밋하거나 롤백한다는 흐름부터 익힌 셈입니다.