본문 바로가기
Java/Spring

Spring) 동시성 제어

by NH_club 2023. 8. 6.
동시성 제어

현재 프로젝트는 아이디어 경매라는 주제이다. 여기에서 입찰을 누르면 그 중 하나만 등록이 되어야 하기 때문에 동시에 입찰을 누르게 되면 문제가 발생한다. 이를 방지하고자 동시성 제어를 해주어야 한다.

동시성 제어 방안

크게 세가지로 분류할 수 있다.
1. synchronized를 활용하여 자바 내부에서 처리하기.
2. DB에 Lock을 걸어 해결하기.
3. Redis로 해결하기

Synchronized

Synchronized는 한 스레드가 한개의 자원을 사용하고 있을 때 다른 스레드들이 접근하지 못하도록 막아주는 동기화 방식이다. 간단하고 직관적이게 데이터 무결성을 보장할 수 있지만, 단점이 크다.
입찰하는 기능은 여러 사용자가 동시에 접근하고 업데이트할 수 있는 공유 자원이다. 따라서 많은 사용자가 접근을 한다면 병목 현상이 발생할 수 있다. 그럴 수록 성능엔 부담이 커지게 되어 안좋은 결과를 초래할 수 있다.

락(Lock)

락에는 비관적 락(Pessimitic Lock)과 낙관적 락(Optimistic Lock)이 있다.


비관적 락
비관적 락은 동시성 문제가 항상 발생할 것으로 가정하고, 데이터에 대한 업데이트가 일어나기 전에 락을 걸어 다른 트랜잭션에서 해당 데이터를 변경하는 것을 막는다. SELECT FOR UPDATE 구문을 이용해 구현할 수 있다. 하지만 비관적 락도 Synchronized와 같이 병목현상을 초래할 수 있다. 비관적 락은 락이 걸려있는 동안 다른 트랜잭션은 해당 데이터에 대한 업데이트를 기다려야 하기 때문에 여러개의 스레드가 동시에 접근하지 못하게 막는 Synchronized처럼 병목현상이 발생할 수 있다.


낙관적 락
낙관적 락은 동시성 문제가 거의 발생하지 않을 것으로 가정한다. 트랜잭션이 커밋되는 시점에 데이터 변경을 확인하고, 변경이 감지되면 충돌이 발생한 것으로 간주하고 에러를 발생시킨다. JPA가 제공하는 version 애노테이션을 사용해서 간단하게 구현이 가능하다. 비관적 락과 Synchronized 처럼 접근을 막는 것이 아니라 병목현상의 위험도는 낮아지지만, 충돌이 발생했을 때 재시도 로직이 따로 필요하며, 충돌이 자주 발생하면 오히려 성능이 저하될 수 있다.
일반적으로 낙관적 락은 JPA가 제공하는 version의 의미를 갖고 있는 컬럼을 사용한다. 읽어올 때 Version이랑 수정 후 트랜잭션이 commit되는 시점에 version이 다르면 충돌이 일어나게 된다.

Redis

Redis는 메모리 기반의 데이터 베이스로서 서버의 역할도 한다. 메모리 기반이기 때문에 디스크 기반의 DB보다 더 높은 성능을 제공한다. 원자적인 연산을 제공하며 이를 통해 동시성 문제를 해결할 수 있다.
단점으로는 메모리 기반이기 때문에 서버가 다운되면 저장된 데이터가 사라질 위험이 있다. 이로 인해 따로 백업하는 방식을 설정해주어야 하며, 구성이 복잡하다. 단일 스레드 모델이기 때문에 여러 코어를 활용하기 위해서는 Redis를 여러개 생성해야한다.

비관적 락 선택 이유

실시간으로 진행되는 경매의 특성상 여러 사용자가 동시에 입찰을 요청할 확률이 매우 높다고 판단했다.
그로 인해서 잦은 충돌이 발생할 것으로 예상 되어 잦은 충돌이 발생하면 성능이 저하되는 낙관적 락은 배제했다.
현재 프로젝트 상황에선 Redis까지 도입하여 관리해야하는 인스턴스를 더 늘릴 필요가 없다. 만약 성능쪽으로 개선이 되어야 된다고 생각이 들 때, 도입하는 방향으로 생각했다.
Synchronized 과 비관적 락 중에서 비관적 락을 선택한 이유는 DB레벨에서 락을 걸기 위함이다.

app 레벨에서 제어하는 것과 DB레벨에서 제어하는 것 둘 중 DB레벨에서 제어하는 것이 데이터 무결성을 유지하는데 더욱 신뢰도가 높다고 판단하였다.

'Java > Spring' 카테고리의 다른 글

Spring) 실시간 통신 방식 선정  (0) 2023.08.08
Spring) 비관적 락으로 인한 오류와 해결  (0) 2023.08.07
Spring) 테스트 코드 작성하기  (0) 2023.08.03
Spring) Spring Security  (0) 2023.07.24
Spring) refresh token  (0) 2023.07.23