동시성 제어는 다수의 프로세스나 스레드가 동시에 동일한 자원에 접근할 때, 데이터 무결성을 보장하기 위해 사용한다.
-프로세스란? 하나의 프로그램이다. 스프링 서버단으로 생각하자면 하나의 컴퓨터(서버)라고 봐도 무방하다.
하나의 서버에 여러 프로세스를 띄울 수 있다. 멀티프로세스이다.
보통은 여러개의 서버에 하나의 프로세스가 각각 있는 것과 동일하다.
-스레드란? 하나의 프로세스 안에서의 작업 단위이다. 일꾼!
Spring으로 말하자면 하나의 request에 하나의 스레드가 할당된다.
-Stateless
HTTP Stateless : HTTP 요청 간의 상태 공유가 안된다.
JWT Stateless : 서버가 상태를 갖고 있지 않다.(JWT가 가지고 있다.)
Server Stateless : 서버는 상태를 가지면 안된다.
-동시성 해결방법
1.Java 언어의 기능 활용 : 실제로는 사용하지 않음. 다른 서버에 변수 공유가 안됨. 지금 우리의 프로젝트는 하나의 서버만 사용하니 가능하지만 실제 서비스에서는 여러대의 서버가 있기에 불가능하다. 사용하지 않는 이유: 서버는 stateless 해야 한다.
2.트랜젝션 격리 레벨 : @Transactional(isolation = Isolation.SERIALIZABLE) , 이 설정(데이터 접근 시 락을 걸어 같은 데이터에 다른 트랜잭션이 동시에 접근할 수 없는 격리 레벨)은 쓰기는 막지만, READ는 막지 못하기 때문이다. 단순 select문은 테이블을 읽어버리기 때문에 마찬가지로 동시성 제어가 되지 못한다.
3.DB(+어플리케이션) 레벨에서의 Lock 제어
*낙관적락 : 사용법이 굉장히 간단하지만 추가 로직이 조금 필요하다.
Counter 엔티티 클래스(아무런 락을 하지 않은 상황)와 OptimisticCounter 엔티티 클래스의 차이는 @Version 사용 여부이다. 하지만 낙관적락 충돌 발생 시 처리 및 예외 카운트 증가(재시도)에 대한 서비스 로직(예외처리)을 추가적으로 작성해야 한다. 작성하지 않으면 재시도를 하지 않고 끝낸다.
*비관적락 : Counter 엔티티 클래스와 PessimisticCounter 엔티티 클래스의 차이는 PessimisticCounter의 레포지토리에서 @Lock 사용 여부이다. 재시도 로직을 작성하지 않아도 된다. 재시도를 기다리고 성공시킨다. 그래서 로직이 낙관적락에 비해 조금 더 간단하다. 대신 무한 대기(데드락)가 걸린다. 우리의 상황에서는 복잡한 로직이 없기에 데드락 발생 가능성이 매우 낮다. 그래서 따로 고려하지 않아도 된다.
=> 하지만, DB와 어플리케이션에 부담이 간다. 그래서 분산락을 쓴다.
4.분산락(DB와 어플리케이션 외 제 3자가 담당)
Redis, Kafka, etcd, MySQL, MongoDB 등의 외부시스템을 이용하여 구현한다.
원리는 Redis가 분산을 담당하고 스프링(어플리케이션)이 DB에게 접근하기 전에 Redis에게 물어본다.
-분산락 사용방법
의존성을 주입한다.
Redisson 라이브러리를 사용한다.(+페어락, Luascript 활용)
RedisConfig를 쓸 필요없다.(스프링 라이브러리에서 디폴트를 포함하기 때문이다.)
에러가 나든 말든 final unlock을 해주어야 한다.
LockManager를 인터페이스로 사용하는 이유는 Redis가 아니라 다른 인프라를 사용할 수도 있기 때문이다.
(도커에서 Redis와 MySQL 띄어서 쓴다.)
스프링 서버와 Redis는 물리적으로 거리가 있다. Luascript를 사용하면 한 번에 요청을 보낼 수 있기에 인프라적인 네트워크 통신 성능이 좋아진다.
'개발 > 부트캠프' 카테고리의 다른 글
본캠프 : Redis 기초 (0) | 2025.04.02 |
---|---|
본캠프 : 어노테이션 정리_freship project (0) | 2025.03.28 |
본캠프 : @EnableJpaAuditing (0) | 2025.03.27 |
본캠프 : methodargumentnotvalidexception (0) | 2025.03.27 |
본캠프 : 개인 과제(플러스 심화) 트러블 슈팅 (0) | 2025.03.20 |