몬그로이

동시성 제어 요약 본문

Organizing Docs/Spring Docs

동시성 제어 요약

Mon Groy 2024. 7. 16. 19:10

동시성 제어가 안 되는 이유
- Spring AOP 로 인해 프록시 객체가 만들어지고
원래 객체인 service의  transactional 때문

bc2인스턴스
자르파일 빌드해서 배포
서버가 한 대 일때는 ok
여러 대일땐 synchronized 안 먹힘

**@sync~~
자바에서 메서드 위에 적으면 한 개씩만 들어가도록 만들어줌 (또는 class 위에 적음)
단, 서버가 여럿이면 제어 안 됨
여러 서버에서 A 메서드에 동시 접근할 수 있으므로
따라서 이 방법 안 씀

DB락(비관적 락)
DB 레코드에서 직접적으로 Lock 걸어 제어
비관적 락 디폴트는 
s-lock 읽기 잠금 @Lock(LockModeType.PESSIMISTIC_READ) in Repository
x-lock 쓰기 잠금 (다른 lock 이 걸려있으면 안 됨)
transactional 과 함께 사용
여러 DB에서는 동시성 제어 불가


낙관적 락
version 컬럼을 추가하여 버전이 다르면 업데이트를 불가능하게 만드는 것
즉, 변경사항이 생기면 버전이 올라가므로 다른 버전으로 확인되어 변경 안 됨
@Version
private int version;
재시도 로직이 필요함 -> 이로 인해 서버 다운 가능
하지만 DB에 직접 락을 걸지 않기 때문에 동시 요청 횟수가 적을 때는 성능 향상에 이점이 있음(즉 충돌이 자주 발생하지 않을 경우 좋음)
비관적 락처럼 @Lock(LockedType.Optimistic_force_increment) 가능하긴 하나
조회, 수정 각각 1씩 오름 ->> 카운트가 2배 따라서 비추





분산락(Ditributed Lock)
임계구역에 Lock 관리 (key 부분에서 관리)
자원에 거는 것이 아니라 행위가 발생하는 곳에 거는 방식으로
Redis 를 활용하는 락임
Mysql 도 가능하지만 Redis 에 비해 성능 떨어짐

Redis 가 가능한 이유
1. MemoryDB
2. 싱글 스레드

Lettuce
SpinLock 사용하므로
spin 돌면서 계속 권한 요청(보챔)하므로 다운 가능

Redisson
한 서버에서만 가지고 있으면 여러 서버에서도 동시성 제어 가능
pub/sub 방식 = Lock 을 가져갈 수 없으면 그냥 대기 (spin 안 함)
lease time 설정 가능
++ Lock 사용하지 않고 redis 싱글 스레드 특성을 이용한 동시성 제어 가능

RedisTemplate
싱글 스레드 특성 이용한 동시성 제어
ops.decrement("counter", "100(재고)"); 을 메서드 블럭에 넣어주면 됨 
트랜젝션 직렬화를 Redis 가 수행함
싱글스레드라서 한 명 뿐이므로 아무리 여럿 들어와도 순차적으로 처리함
In-memory 가(?) 굉장히 빠르게 하나씩 100개 수행함
싱글 스레드이므로 getCounter->setCounter 가 한 세트로 움직여버리는 느낌
getCounter 에서 setCounter 로 넘어가는 중에 새로운 getCounter 를 수행하지 않음


=========
1. Synchronized : method 또는 class 위에 선언하면, 단일 쓰레드만 접근하도록 해서
- 한계점: 서버가 여러대면 동시성 제어가 불가능 (쓰레드 30개)
         -> A 서버 A Synchronized Method 접근 (단 1개만)
         -> B 서버 A Synchronized Method 접근 (단 1개만)
         -> C 서버 A Synchronized Method 접근 (단 1개만)
2. DB Lock
    1. 비관적 락 (Pessimistic Lock) : Shared Lock(s-lock), Exclusive Lock(x-lock) 기반으로 DB 레코드 직접적으로 잠금을 걸어서 다른 트랜잭션들이 접근하지 못하게 함.
    2. 낙관적 락 (Optimistic Lock) : Version 컬럼을 통해 데이터가 수정되었는지 확인 후, 수정되었으면 (Version이 일치하지 않으면) 롤백 및 예외 발생. -> catch 재시도
- 한계점: 락으로 인한 성능 저하, 항상 성립하는 건 아님


3. 분산락
    1. Lettuce: Spin Lock -> 락 내놔 계속 보채는거죠 -> Redis 부하가 집중됨.
    2. Redisson: pub/sub -> 락을 얻지 못하면 안 보채고 대기. -> Lock을 획득할 수 있는 상태가 되면 (lock.unlock()) Redisson 이 구독자들한테 알려줌 (락 가질사람 ~?)
    3. RedisTemplate: Redis 의 싱글 스레드 특성을 활용 -> 동시다발적으로 요청이 들어와도 결국 그 요청을 처리하는 스레드는 1개이기 때문에, 어쩔수 없이 순차적으로 처리함 -> 동시성 제어