티스토리 뷰
토이 프로젝트를 진행하던 중, 동시성 이슈를 해결해야 할 필요가 생겼습니다.
이 내용을 문서화 할 필요도 있어 해당 내용을 정리한 내용을 블로그에다가도 옮겨 보았습니다.
Objective
현재 우리 서비스에서는 하나의 책에 2개 이상의 스레드가 접근해 주문을 시도하면 데이터의 정합성이 깨지는 문제가 있음. 우리 서비스와 같은 다중 사용자 환경에서 둘 이상의 트랜잭션이 동시에 수행될 때, 데이터의 정합성을 해치지 않도록 트랜잭션의 데이터 접근을 제어할 필요가 있음. 이러한 동시성 이슈를 해결하기 위해 lock을 이용한 동시성 제어를 사용하고자 함.
Proposal
- redisson 사용
redis의 pub-sub 기반 message broker 기능을 이용하여 락을 구현하는 방법이다. 락을 해제하는 측이 락을 대기하는 프로세스에게 락 획득을 시도해도 된다는 메시지를 전달하는 방식으로 동작하며, 이 방법을 사용하면 끊임없이 redis 서버에 락 획득이 가능한지 여부를 확인하는 spin lock을 사용하지 않아도 된다.
이 방법은 우리가 직접 구현할 필요가 없이 redisson이라는 라이브러리를 사용하면 된다. 이미 메시지 브로커 기능을 활용하여 락을 구현해 두었기 때문이다.
또한 이 라이브러리는 타임아웃을 구현하여 일정 시간동안 락을 획득하지 못하면 예외를 발생시킬 수 있다. (pessimistic lock에서도 timeout을 구현할 수는 있다고 함. 다만 락을 유지하는 동안 리소스를 계속 점유하니 db에 계속 부하를 주게 되는 문제가 있다고 함.)
이를 통해 특정 서버에 문제가 생겨도 처리할 수 있어 해당 방법을 사용하는 게 적합하다고 생각된다.
- pessimistic lock 사용
실제로 데이터에 락을 걸어 정합성을 맞추는 방법이다. 락을 걸게 되면 다른 트랜잭션에서는 락이 해제되기 전까지는 데이터를 건들 수 없다.
락을 통해 데이터를 제어하기 때문에 데이터의 정합성을 보장할 수는 있으나, 데이터 자체에 락이 걸리기 때문에 성능 저하가 발생할 수 있다.
또한, 서로 다른 스레드에서 각자 락이 걸린 데이터에 접근할 때 데드락이 발생할 수 있으며 락을 걸어둔 서버에 장애가 발생하면 해당 데이터에 대한 락이 풀리지 않아 다른 서버에서 해당 데이터를 수정할 수 없는 상황이 발생할 수 있다.
checkPoints
redis를 활용하게 되면 별도의 구축비용과 관리비용이 발생한다. 락을 라이브러리 차원에서 제공하기 때문에 사용법 숙지가 필요하다.
alternatives
- optimistic lock
실제 lock을 이용하지는 않고 버전을 이용해 정합성을 맞추는 방법이다.
버전 컬럼을 추가해야 하고, 재시도 로직을 직접 작성해야 한다. 우리 서비스의 경우 트랜잭션에서의 충돌이 빈번할 것이기 때문에 성능이 저하될 것으로 예상되어 적합하지 않다.
- named lock
사용되는 데이터에 락을 거는 게 아니라 별도의 다른 데이터에 락을 거는 방법
락은 별도의 트랜잭션으로 분리해서 관리해야 하며 트랜잭션이 종료될 때에 락 해제를 꼭 해줘야 한다.
우리 서비스에서 실제로 사용할 시에 구현방법이 복잡해 적합하지 않다고 생각하였다.
- Lettuce
스프링에서 redis를 사용하면 lettuce가 기본이기에 별도의 라이브러리를 사용하지 않아도 된다.
다만 spin lock 방식이기 때문에 동시에 많은 스레드가 lock 획득을 대기중이라면, redis에 부하가 갈 수 있어 적합하지 않다고 생각하였음.
- syncronized
해당 키워드는 해당 스레드를 제외하고 나머지 스레드들의 접근을 막아 순차적으로 접근할 수 있게 해준다.
다만 하나의 프로세스 안에서만 보장이 되기 때문에 우리와 같은 환경에는 적합하지 않다.
references
locking 기법에 대해
https://mangkyu.tistory.com/30
mysql의 locking
https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html
jpa lock(하이버네이트) https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#locking
lock 전반에 대한 내용. 이해하기 쉬움
https://velog.io/@tco0427/동시성-제어하기
https://thalals.tistory.com/370
redis lock
https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html
https://velog.io/@hkyo96/Redis-분산락을-이용한-동시성-문제-해결
'spring' 카테고리의 다른 글
스프링부트에서 Selenium 사용하여 크롤링하기 (0) | 2023.03.30 |
---|---|
[spring security] failed to load resource: the server responded with a status of 405 (0) | 2023.02.04 |
수동으로 등록한 Bean과 자동으로 등록한 Bean 이름이 겹치면? (0) | 2023.01.10 |
@Nested 테스트코드를 더 보기 쉽게 (0) | 2023.01.07 |
UnsatisfiedDependencyException 의존성 추가하지 않았을 때 발생 (0) | 2023.01.06 |
- Total
- Today
- Yesterday
- jmeter세션
- 프로그래머스
- 항해
- jmeter토큰
- 부하테스트시나리오
- Python
- bankersRounding
- jwt
- index
- Java
- jmeter쿠키
- EC2
- Redis
- CheckedException
- jmeter테스트
- Spring
- 스프링faker
- hackerrank
- Redisson
- 대규모더미데이터
- jmeter시나리오
- 동적크롤링
- Lock
- jmeter부하테스트
- 인덱스
- pessimisticlock
- 토큰
- 자바
- CorrectnessAndTheLoopInvariant
- jmeter로그인
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |