재고시스템으로 알아보는 동시성이슈 해결방법

존스노우·2023년 10월 26일
0

  • 동시에 여러개의 요청이 들어오면 어떻게 될까?

  • 실패를 하게 된다
  • 레이스 컨디션이 일어났기때문
  • 둘 이상 스레드가 공유 데이터에 엑세스할 수 있고
  • 동시에 변경을 하려고 할때 발생하는 문제때문.

  • 예상 그림

  • 갱신되기 전에 값을 가져가서 둘다 100에서 1을 빼기 때문에
  • 갱신이 누락된다.
  • 하나 스레드가 작업이 완료후 다른 스레드가 작업 완료되게 하자

Synchronized

  • 될까 ?

  • 트랜잭션 애노테이션 동작방식 때문에 실패함
  • 스프링에서는 우리가 만든클래스를 래핑할 클래스를 새로만들어서 실행함.
  • 스탁서비스를 필드로가지는 클래스를 새로만들어서 실행함
  • 트랜잭션 시작한후 메소드호출하고 메소드 실행이 종료하게된다면
  • 트랜잭션을 종료하게 됨.
  • 여기서 문제!
  • 트랜잭션 종료시점에 데이터베이스를 업데이트하지만
  • 디크리스 메소드가 완료되기전에 다른 스레드가 디크리스 메서드를 호출 할수 있다.
  • 그런다면 다른 스레드는 갱신되기전 값을 가져가서 동일한 문제가 발생함

  • 트랜잭셔널을 주석처리하면
  • 성공한다

  • 자바의 synchronized는 하나의 프로세스 안에서만 보장 됨
  • 서버가 2대 그 이상일 경우 데이터 접근이 여러대에서 할 수 있게 됨

  • 이런식으로 갱신되지 않는 값을 들어가서 새롭게 다시 갱신된다.
  • 싱크로나이즈드는 각 프로세스 안에서만 보장되기 때문에
  • 결국 여러 스레드에서 동시에 데이터 접근이 가능해서 레이스 컨디션이 발생하게 됨
  • 실제 운영중인 서비스는 2대이상이기 때문에 싱크로나이즈드는 사용하지 않는다.

Mysql

Pessimistic Lock

  • 서버1이 락을 걸면 나머지는 들어가지 못함
  • 실제로 데이터의 락을 걸어서 데이터 정합성을 맞춤

  • 이렇게 작성하고
  • service를 바꿔준뒤 아까 작성한 테스트를 실행하면

  • 이 부분이 락을 걸고 데이터를 가져오는 부분

장점

  • 충돌이 빈번하게 일어나면? 옵티미스팅 락보다 성능이 좋다
  • 데이터 정합성이 보장됨
  • 별도의 락으로 인한 성능 감소

Optimistic Lock

  • 내가 읽은 버전에서 수정사항이 생겼을 경우 어플리케이션에서 다시 읽은후 수정
  • 업데이트 수행하게되면 where 절에 version 조건이 들어감
  • 동시에 버전이 올라감
  • 버전이 틀리면 수정 실패
  • 이 부분은 @Version 어노테이션만 추가하면됨

  • 이렇게 쿼리문도 작성해주고

  • 재시도 로직이 있어서 좀 더 걸리지만 성공
  • 비관적 잠금보다 성능이 우수
  • 재시도 로직 작성해 줘야됨 -> 단점
  • 충돌이 그리 심하지않으면 낙관적락을 사용하는게좋음

Named Lock

  • 이름을 가진 락
  • 이름을 가진 락을 획득후 해제할때까지 다른 세션은 접근을 못함
  • 트랜잭션이 끝나도 해제가 안돼서 별도 수동으로 입력하거나 시간이 지나야된다.
  • 메타데이터의 락킹 방법

  • 별도 공간에 락을 검
  • 세션 1이 1이라는 이름으로 락을 거면
  • 세션 1 이 종료하고 세션2가 접근 가능함
  • 실무에서는 데이터소스를 분리해서 사용해야된다
  • 이번 예제는 분리하지 않음

  • 락 획득후 재고 감소
  • 모든 로직 종료시 락 해지

  • 부모의 트랜지션과 별도로 실행되어야 되서 프로게이션 변경

  • 같은 데이터소스를 사용하기 때문에 넉넉하게 40

  • 테스트는 성공
  • 주로 분산락 구현할때 사용하는
  • 타임아웃 손쉽게 구현(비관적락은 구현하기 어렵다)
  • 데이터 삽입시 정합성 맞춰야 하는경우 사용한다.
  • 락 해제 세션 관리를 해줘야하고 구현방법이 복잡할 수 있다.
profile
어제의 나보다 한걸음 더

0개의 댓글