지난 포스팅에서 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에 대해서는 내가 작성하기보다 참고한 포스트를 남기는게 더 좋을 듯 하다.
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 |