티스토리 뷰

문제 상황

  • 동시에 여러 번 호출할 경우 DB 데이터가 중복 저장되는 현상 빈번히 발생
  • 한 번의 요청에 종류가 다른 4가지 멤버의 기여도를 가져와 하나의 같은 테이블을 업데이트, 다른 한 테이블에는 insert

DB Lock 적용

  • DB Lock을 통해 데이터의 중복 저장을 막을 수 있을 것이라 기대

시도 1 : @Version 기반 낙관적 락

  • 성능을 생각해 낙관적 락을 먼저 걸어보기로 했다.
  • Jpa 엔티티에 @Version 필드를 추가해 락을 적용한다.
  • 다중 요청시 version 필드의 충돌로 인해 롤백이 자주 발생했다.
    • 쓰기 작업에 더 높은 수준의 락이 필요하다고 느꼈다.

시도 2: 공유락

  • 읽기 작업에는 락이 걸리지 않아도 되었기에 공유락을 선택했다. (이전의 데이터를 읽어도 큰 상관은 없었다.)
  • 사용할 JpaRepository의 메소드에 다음과 같이 추가했다. (베타적 읽기 락은 공유락을 적용해준다.)
@Lock(LockModeType.PESSIMISTIC_READ)
  • 동시 삽입/업데이트 시도는 제어가 가능해져 동시성 문제는 해소되었다.
  • 하지만, 데드락이 간헐적으로 발생했다.

데드락 해소를 위한 시도

  • 락을 적용한 메소드에 timeout 설정을 걸어봤다.
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")})
  • 데드락으로 계속해서 서로 기다리는 현상이 짧아졌다. 다른 방안을 생각하며 개발하는 도중 아키텍처에 Redis가 추가되었다.
  • Redis 로도 락을 적용할 수 있기 때문에, 고려하기로 했다.

시도 3: Redis 분산락

  • 중복 저장 현상은 없어졌지만, DB에 Lock을 사용하기보다는 Redis를 사용하는 것이 성능적으로 개선될 것이라 기대했다.
  • 다중 서버였기에 분산락을 적용했다.
    • 분산락은 처음 락을 획득한 요청이 우선 처리되도록 구현했다.
    • 다음 요청에 우선권을 주면 무수히 많은 요청이 오는 경우에 정상 처리되는 요청이 계속 바뀌며 처리가 늦어질 수 있다.

세부 구현 (전파 레벨과 락 해제 시점)

  • 분산락은 어노테이션과 AOP를 통해 구현하여 어노테이션이 붙은 메서드에서 동작하도록 설정했다.
  • 또한, 전파 레벨을 REQUIRES_NEW 로 만들어 기존 트랜잭션 유무와 상관 없이 새로운 트랜잭션이 생성되어 그 안에서 실행되며, try-catch-finally의 finally에서 락을 해제하여 해당 새로운 트랜잭션이 끝난 이후 락을 해제하도록 하였다.
@Transactional(propagation = Propagation.REQUIRES_NEW)

 

왜 트랜잭션이 끝나고 락을 해제할까?

  • 첫번째 요청의 트랜잭션이 끝나기 전에 락을 해제하고 두번째 요청이 락을 점유하게 되면, 두번째 요청의 락 점유 이후에 커밋이 되어 데이터 정합성이 깨질 수 있다.
  • 커밋 시점에 두번째 요청이 락을 점유한다는 것은, 커밋 이전에 두번째 요청이 데이터를 읽어왔기에 변경 이전의 데이터를 읽은 것이기 때문이다.

결과

  • 불안정하게 처리되었던 동시 요청을 안정적으로 처리가 가능해졌다.

이러한 과정을 통해...

  • 여러 락 종류에 대해 학습해볼 수 있었고, 데이터 정합성에 대한 고민도 할 수 있어 뜻깊었다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함