트랜젝션

 

데이터를 저장할 때 단순 파일이 아닌 데이터베이스에 저장하는 가장 큰 이유는 데이터베이스가 트랜젝션이라는 개념을 지원하기 때문이다.

트랜젝션은 데이터베이스에서 하나의 거래를 안전하게 처리하도록 보장해주는 것을 뜻하는데 트랜젝션 기능을 사용하면 중간에 문제가 생길 경우 시작 이전으로 되돌릴 수 있다. 작업이 완료되고 데이터베이스에 정상 반영되는 것을 커밋(Commit)이라 하고, 문제가 생겨서 작업 이전으로 되돌리는 것을 롤백(Rollback)이라 한다.

 

 

트랜젝션 ACID 

 

트랜젝션은 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)를 보장해야 한다.

  • 원자성: 트랜잭션 내에서 실행한 작업들은 마치 하나의 작업처럼 모두 성공 하거나 모두 실패해야 한다.
  • 일관성: 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 한다. 예를 들어 데이터베이스에서 정한 무결성 제약 조건을 항상 만족해야 한다.
  • 격리성: 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리한다예를 들어 동시에 같은 데이터를 수정하지 못하도록 해야 한다격리성은 동시성과 관련된 성능 이슈로 인해 트랜잭션 격리 수준(JPA  16.1 트랜잭션과 락 참고)을 선택할 수 있다.
  • 지속성: 성공적으로 트랜젝션이 끝나면 그 결과가 항상 기록되어야 한다중간에 시스템에 문제가 발생해도 데이터베이스 로그 등을 사용해서 성공한 트랜잭션 내용을 복구해야 한다.

 


 

데이터베이스 연결 구조와 DB 세션

 

데이터베이스는 커넥션을 연결할 때 내부에 DB 세션을 생성(커넥션 풀이 10개의 커넥션을 생성하면 세션도 10개 생성)하는데 트랜젝션을 시작하고, SQL을 실행하고, 커밋, 롤백, 트랜젝션 종료 등의 모든 요청은 DB 세션을 통해 실행이 된다. 

 

트랜젝션 내부 동작

 

데이터베이스는 커밋을 호출하기 전까지 데이터를 임시로 저장하는데 해당 트랜젝션을 시작한 세션만 변경 데이터(등록, 수정, 삭제)가 보이고 다른 세션은 변경중인 데이터가 보이지 않는데 이는 문제가 생겨서 트랜젝션 롤백이 되는 경우 데이터 정합성에 큰 문제가 생기기 때문이다.

 

트랜젝션에는 자동 커밋과 수동 커밋이 있는데 자동 커밋의 경우 말 그대로 쿼리를 하나 하나 실행할 때마다 자동으로 커밋을 해줘서 편리하지만 트랜젝션 개념이랑 맞지 않기 때문에 수동 커밋 모드(set autocommit false)로 바꾸어 사용하는 것이 트랜젝션의 시작이라고 볼 수 있다.

 

 

DB 락

 

한 세션에서 트랜젝션을 시작하고 데이터를 수정하는 동안 다른 세션이 같은 데이터를 수정하게 되면 트랜젝션의 원자성이 깨지는데 이런 문제를 방지하려면 트랜젝션이 커밋이나 롤백을 하기 전까지 다른 세션에서 해당 데이터를 수정할 수 없도록 막아야 한다.

 

데이터베이스는 이런 문제를 해결하기 위해 락(Lock)이라는 개념을 제공하는데 세션은 트랜젝션을 시작하고 값을 변경하려는 데이터의 row에 대해 먼저 lock을 얻는데 락을 갖고 있는 동안 다른 세션은 해당 row의 데이터를 변경할 수 없다. 락을 획득한 세션이 트랜젝션을 종료하면 락이 반납되고 다른 세션은 대기하다가 락을 획득한 뒤에 데이터를 변경할 수 있으며 락 대기 시간이 넘어가면 타임아웃 오류가 발생한다.

 

데이터베이스마다 다르지만, 락이 걸린 동안에는 다른 세션에서 데이터를 변경하지 못하고 지연이 걸리기 때문에 일반적인 데이터베이스는 조회를 할 때는 락을 사용하지 않고 바로 데이터를 조회할 수 있도록 한다.  조회를 할 때도 락이 필요한 경우  'select for update' 구문을 사용하면 되는데 조회를 통해 중요한 계산을 수행하는 경우 조회 시점에 락을 획득하면 된다.

 


 

트랜젝션 적용

 

트랜젝션은 비즈니스 로직이 있는 서비스 계층에서 시작해야 한다. 비즈니스 로직이 잘못되면 문제가 되는 부분을 함께 롤백해야 하기 때문이다. 서비스 로직에서 트랜젝션을 시작하려면 먼저 커넥션을 획득하고 유지해야 하는데 애플리케이션에서 같은 커넥션을 유지하려면 커넥션을 파라미터로 전달해서 같은 커넥션이 사용되도록 유지해야 한다.

 

하지만 이러한 방법은 서비스 계층이 매우 복잡해지고 커넥션을 유지하기도 어려워지는데 스프링을 사용해서 트랜젝션을 편리하게 사용할 수 있다.

 

 

[참고]  인프런 김영한님 강의를 공부한 내용입니다.

+ Recent posts