Spring

Database Transaction과 데이터 동기화 -2

라우브 2021. 8. 1. 12:51

지난 포스팅에서 Database Transaction이 무엇인지 알아보았다.

참고: https://ldh-developer.tistory.com/30

 

Database Transaction과 데이터 동기화 -1

트랜잭션이란? 데이터베이스가 수행할 연산들의 논리적인 작업 단위(하나의 질의가 아닐 수 있다) 트랜잭션의 특징 원자성(Atomictiy) 트랜잭션이 데이터베이스에 모두 반영이 되거나, 하나도 반

ldh-developer.tistory.com

Transaction의 기본적인 개념들은 인터넷에 워낙 자료들이 많아서 굳이 자세하게 다루지는 않았다.

 

 

문득, Transaction에 대한 이론은 알고있었지만 내가 알던대로 제대로 동작하고 있을까 ? 라는 의문이 들었다.

눈으로 직접 확인하는게 가장 좋을 것 같아서 직접 테스트 해 보았다.

 

실험에 앞서, 실험에 사용한 도메인 객체는 아래와 같다.

웹툰이나 영화의 리뷰 혹은 댓글 정도로 생각하면 될 것 같다. (작성자 정보, 작성 시간 등 다른 속성들이 필요하겠지만 실험에서는 그런 부분이 중요한 게 아니니 생략했다)

 

 

Test1.  JdbcTemplate 을 이용한 DB 데이터 동기화 테스트

 

위와 같이 JdbcReviewRepository 와 ReviewService를 구성하고 ReviewController에서 아래와 같이

thread 50 개를 생성한 후, 1번 review에 대해 recommend 함수를 100번 실행하도록 구현했다.

결과는 아래와 같다.

ㅎ... 해당 데이터에 lock 이 제대로 걸리지 않아서, 동기화 처리가 정상적으로 되지 않았다. (동기화 처리가 정상적으로 되었다면 recommend가 100이 나와야 함)

 

Database Lock 에 대해 조금 더 공부해 보았다.

참고 : https://suhwan.dev/2019/06/09/transaction-isolation-level-and-lock/

 

Lock에는 Shared Lock 과 Exclusive Lock이 있는데, Shared Lock 은 read에 대한 lock이고 Exclusive Lock은 write에 대한 lock이다. 즉 내가 원하는 작업을 진행하려면 저 1번 Review에 대해 Exclusive lock을 걸어야 한다.

 

sql에서 Exclusive lock을 걸 때는 아래와 같이 select 문의 마지막에 "FOR UPDATE" 예약어를 작성해야 한다.

select * from review where id = ? FOR UPDATE

자 그럼 이것을 JdbcReviewRepository에 적용해 봐야겠다.

 

Test2. JdbcTemplate에 선언적 Exclusive 락 걸기

JdbcReviewRepository의 query부분을 수정한 후 다시 50개의 Thread를 만들어 recommend() 함수를 100회 실행해 보았다.

원하던 대로 해당 Data에 동기화 처리가 잘 진행되는 것을 알 수 있었다. 

 

최근 지원한 회사의 과제 테스트에서 JdbcTemplate을 이용하여 과제 테스트를 진행해야 했고, 데이터 동기화에 대한 요구사항이 있었다. 당시엔 요구사항을 충족시키지 못해서 정답을 제출하지 못했지만, 그 덕에 내가 Transaction, Database 데이터 동기화 쪽에 아직 지식이 얕다는 것을 알게되었다 ㅎㅎ 

 

그런데 나는 개발할 때 JdbcTemplate을 사용하지 않는다 ... JPA를 이용한 개발을 선호하는데 그럼 JPA에서 데이터 동기화 작업을 진행하려면 어떻게 해야할까

 

 

Jpa의 lock에 대해서는 내가 작성하기보다 참고한 포스트를 남기는게 더 좋을 듯 하다.

참고 : https://velog.io/@recordsbeat/JPA%EC%97%90%EC%84%9C-Write-Skew-%EB%B0%A9%EC%A7%80%ED%95%98%EA%B8%B0-locking-%EC%A0%84%EB%9E%B5

 

Test3. Jpa Repository에 Pessimistic lock 걸기

위와 같이 Review를 find하는 함수에 @Lock(value = LockModeType.PESSIMISTIC_WRITE)를 적용하면 해당 데이터에 대해 배타 락을 적용할 수 있다.

Query 문을 로그로 찍어보면 실제로 select 문 뒤에 for update 예약어가 작성됨으로써 배타락을 적용하고 있음을 알 수 있었다. 

마찬가지로 위와 같이 스레드를 50 개 생성한 후 100 회의 recommend()를 실행하면, 아래와 같이 원하던 결과를 얻을 수 있었다.

 

 

 

 

결론. 

  • lock에는 shared lock과 exclusive lock(배타락)이 있다. shared lock은 read, exclusive lock은 write에 대한 lock이다.
  • Transaction 이 실행되는 동안, 해당 Transaction에서 읽어온 데이터에 대해 다른 Transaction에서 수정을 못하게 하려면, 배타락을 적용해야 한다.
  • sql 에서 배타락을 적용할 때는 select 문 마지막에 "for update"를 붙여야 한다.
  • JPA에서 배타락을 적용할 때는 낙관적 락, 비관적 락, 암시적 락, 명시적 락 중 원하는 것을 적용해야 한다.
 

JPA 잠금(Lock) 이해하기

JPA(Hibernate:하이버네이트)에 의한 잠금(Lock:락) 사용중에 생각하고 있던바와 동작이 좀 다른 부분이 있어서 전반적으로 정리해 보았습니다. 잠금(Lock)의 종류 낙관적 잠금(Optimisstic Lock) 낙관적 잠

reiphiel.tistory.com

Database data에 동기화 처리하는 방법은 알게 되었지만, 여전히 왜@Transactional(isolation=Isolation.REPEATABLE_READ)를 적용했을 때 동기화 처리가 안되는지에 대한 해답은 얻지 못했다.  해답을 알아낼 때까지는 직접 배타락을 걸어 주어 동기화 처리를 해야겠다. (이 문제의 원인을 아시는 분 댓글 달아주시면 정말 감사하겠습니다. )

 

 

'Spring' 카테고리의 다른 글

Filter, Interceptor, AOP  (0) 2021.08.26
Database Transaction과 데이터 동기화 -1  (0) 2021.08.01
스프링 MVC 구성 요소  (0) 2020.08.02
DispatcherServlet이란  (0) 2020.08.02
서블릿 리스너와 서블릿 필터  (0) 2020.08.01