✔️ 더이상 나눌 수 없는 작업의 단위
계좌이체의 경우, 출금과 입금이 하나의 트랜잭션으로 묶여야 된다.
❓ 만약 출금, 입금 둘다 각각의 트랜잭션을 가지고 있다면?
➡️ 출금은 성공했는데 입금에 실패했다하면 출금만 되고 입금이 안되는 것이다. 즉, 나는 돈을 보냈는데 상대방은 돈을 받지 못하는 상황이 되어버린 것이다.
➡️ 둘 중 하나라도 되지 않으면 출금하기 전으로 돌아가서(트랜잭션이 시작하기 전) Rollback을 해줘야 한다. 원래대로 돌아가야 한다.
➡️ 모 아니면 도: 출금과 입금이 모두 성공하지 않으면 실패
둘 이상의 작업을 어떻게 하나의 트랜잭션으로 묶는지 알아보자!
✔️ 나눌 수 없는 하나의 작업으로 다뤄져야 한다.
✔️ 트랜잭션 수행 전과 후가 일관된 상태를 유지해야 한다.
✔️ 각 트랜잭션은 독립적으로 수행되어야 한다.
각 트랜잭션이 영향을 받지 않아야 함
Isolation Level
: 어느정도 영향을 끼치는 정도
: 레벨이 꼭 높다고 좋은 것은 아님
➡️ 작업에 따라 적절하게 처리해야 함
✔️ 성공한 트랜잭션의 결과는 유지되어야 한다.
✔️ Commit
: 작업 내용을 DB에 영구적으로 저장
✔️ rollback
: 최근 변경사항을 취소(마지막 커밋으로 복귀)
✔️ AutoCommit
: 명령 실행 후, 자동으로 커밋이 수행(rollback 불가)
✔️ AutoCommit 해제: 수동 commit
: 명령 실행 후, 명시적으로 commit 또는 rollback을 입력
SET autocommit = 0;
✔️ 각 트랜잭션을 고립시키는 정도를 4가지로 나눔
✔️ 트랜잭션의 종류에 따라서 4가지의 레벨을 선택할 수 있다.
Tx1이 하는 작업에 Tx2가 영향을 줬다.
➡️ isolation level이 굉장히 낮음
dirty read 라고도 불림
: 다른 사람이 손대서 더러워졌다는 의미
커밋된 데이터만 읽기 가능
Tx1 입장에서는 없던 데이터가 갑자기 나타남
➡️ Phantom read라고도 불림
트랜잭션이 시작된 이후 변경은 무시됨
반복해서 읽을 수 있다는 뜻
: 다른 트랜잭션에서 아무리 데이터를 넣고 커밋을 해도 해당 트랜잭션에서는 같은 결과만 나옴
고립도 최상
각 트랜잭션이 하나도 겹치지 않고 고립적으로 수행 ➡️ 직렬
: 트랜잭션은 좀 겹치게 움직여야 함(병렬). 그래야 적은 하드웨어 저장으로 많은 사람들이 사용할 수 있다. 하지만 데이터의 품질이 떨어질 수 있다.
데이터가 절대로 엉키면 안되고 중요한 트랜잭션으로 처리해야 한다면 SERIALZABLE
을 써야 한다.
➡️ 성능 ⬇️ 품질 ⬆️
Tx1에서 누가 읽어오는 작업을 하고 있는데 Tx2에서 Insert를 했다. 그럼 바로 넣어지지 않고 대기 상태가 된다. Tx1에서 commit을 해서 작업이 다 끝나야 대기 상태가 풀리고 그때서야 들어가게 된다.
@Test
public void transactionTest() throws Exception {
int rowCount = 0;
Connection connection = null;
try {
deleteAll();
connection = dataSource.getConnection();
// 여러개의 문장을 하나의 트랜잭션에 묶을 수 있게 수동 commit으로 바꿈
connection.setAutoCommit(false);
String sql = "insert into user_info values (?, ?, ?, ?, ?, ?, now())";
// 1번째 테스트
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "asdf9");
preparedStatement.setString(2, "1234");
preparedStatement.setString(3,"aaa");
preparedStatement.setString(4, "aaa@aaa.com");
preparedStatement.setDate(5, new java.sql.Date(new Date().getTime()));
preparedStatement.setString(6, "faceBook");
rowCount = preparedStatement.executeUpdate();
// 2번째 테스트
preparedStatement.setString(1, "asdq");
rowCount = preparedStatement.executeUpdate();
// 둘 중에 하나라도 false가 되면 예외가 뜬다.
connection.commit();
} catch (Exception e) {
connection.rollback();
} finally {
}
}