시냅스

Java 트랜잭션 관련 정리 본문

Java, Spring

Java 트랜잭션 관련 정리

ted k 2022. 8. 25. 16:33

트랜잭션 Transaction 이란

  • 트랜잭션은 하나의 거래를 안전하게 처리하도록 보장하는 것을 뜻한다.
  • 은행 송금을 예시로 하자면, 송금하는 주체는 돈이 감소해야 하고, 동시에 송금을 받는 주체는 돈이 증가해야 한다.
  • 두 트랜잭션이 성공적으로 수행됐다면 commit 해야 하고, 하나라도 실패했다면 rollback해야 한다.

 

트랜잭션 ACID

  • 트랜잭션은 원자성 (Atomicity), 일관성 (Consistency), 격리성 (Isolation), 지속성 (Durability) 를 보장해야 한다.
  • 원자성
    • 트랜잭션 내에서 실행한 작업은 마치 하나의 작업인 것처럼 모두 성공하거나 실패해야 한다.
  • 일관성
    • 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 한다.
    • 예를 들면 데이터베이스에서 정한 무결성 제약 조건을 항상 만족해야 한다.
  • 격리성
    • 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리한다.
    • 동시에 같은 데이터를 수정하지 못하는 것과 같다.
    • 격리성은 동시성과 관련된 성능 이슈로 인해 트랜잭션 격리 수준(Isolation level) 을 선택할 수 있다.
  • 지속성
    • 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 한다.

 

트랜잭션 격리 수준 Isolation level

  • READ UNCOMMITED 커밋되지 않은 읽기
  • READ COMMITTED 커밋된 읽기 - 일반적으로 많이 사용되는 level
  • REPETABLE READ 반복 가능한 읽기
  • SERIALIZABLE 직렬화 가능

 

트랜잭션에 대한 이해

  • WAS, DB 접근 툴 등 클라이언트에서 DB 서버에 접근할 경우 커넥션이 생길 때마다 각각은 개별 세션을 갖는다.
  • 클라이언트에서 SQL을 전달하면 커넥션에 연결된 세션에서 실행한다.
  • 트랜잭션은 커밋 또는 롤백을 통해야만 종료할 수 있다.
  • 만약 sql을 실행하고 commit 을 호출하지 않았다면 임시로 데이터를 저장하는 것이다.
    • 다른 세션에서는 임시로 저장된 데이터는 확인할 수 없다.
      • 이러한 특성은 수동 커밋 설정에서만 확인할 수 있다.
      • 보통 자동 커밋 모드가 기본으로 설정된 경우가 많기 때문에 수동 커밋 모드로 설정하는 것을 트랜잭션을 시작한다고 표현한다.
  • 만약 sql 실행 중 에러가 발생되면 rollback을 통해 원복해야하는데, 자동 커밋 모드에서는 불가능하다.
    • 원자성 (모두 실패하거나 모두 성공해야 한다.)이 지켜져야 한다.

 

DB 락

  • 동시에 같은 데이터를 수정하지 못하도록 하여 데이터 정합성을 지키도록 한다.
  • lock은 먼저 접근한 세션에서 commit 하거나 rollback해야 획득할 수 있다.
  • 위 사진 5번에서 세션2는 lock을 획득하기 위해 대기한다.
    • 대기하는 시간은 설정할 수 있다.
    • 세션 1이 6번에서 커밋하여 세션 2가 lock을 획득할 수 있게 한다.
  • 일반적으로 조회에서는 사용하지 않지만 select for update 를 통해 lock 을 획득할 수 있다.

 

실제 적용

  • 트랜잭션을 사용하는 동안 같은 커넥션을 유지해야 한다.
  • 가장 단순한 방법은 커넥션을 파라미터로 전달해서 같은 커넥션을 유지하도록 하는 것이다.
    • 비즈니스 로직이 완료되면 connection 을 닫아주면 된다.

  • 다만 JDBC에서 JPA로 기술을 변경하게 되는 경우 트랜잭션 추상화를 이용할 수 있다.

  • 트랜잭션 추상화를 이용하면서 connection 과 같은 리소스를 동기화할 필요가 있다.
  • 이 때 스프링은 트랜잭션 동기화 매니저를 제공한다.
    • 트랜잭션 동기화 매니저는 쓰레드 로컬을 사용하면서 멀티쓰레드 상황에 안전하게 커넥션을 동기화할 수 있게 한다.
  • 정리하면
    • 1. 트랜잭션 메니저는 데이터소스를 통해 커넥션을 만들고 트랜잭션을 시작한다.
    • 2. 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다.
    • 3. 레파지토리는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내 사용한다.
    • 4. 트랜잭션이 종료되면 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션 또한 닫는다. ( DataSourceUtils.releaseConnection() )

 

트랜잭션 프록시

  • 서비스 계층에 순수한 비즈니스 로직만 남기기 위해 스프링 AOP를 통해 프록시를 도입할 수 있다.
  • 트랜잭션이 필요한 곳에 @Transaciotnal 어노테이션을 붙여 적용시킬 수 있다.

 

    @Transactional
    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        bizLogic(fromId, toId, money);
    }

Comments