인프런 "재고시스템으로 알아보는 동시성 이슈 해결방법" 강의를 보고 정리한 글입니다.
📗 Spring & Java, Mysql, Redis 를 이용합니다
- 재고시스템을 활용해서, 멀티스레드 혹은 분산환경에서 가변데이터에 접근하는 동시성 문제를 해결하는 내용이 강의에 담겼습니다.
동시성 문제란, 동일한 하나의 데이터에 2 이상의 스레드, 혹은 세션에서 가변 데이터를 동시에 제어할 때 나타는 문제로,
하나의 세션이 데이터를 수정 중일때, 다른 세션에서 수정 전의 데이터를 조회해 로직을 처리함으로써 데이터의 정합성이 깨지는 문제를 말합니다.
📌 강의를 듣고 느낀 점은, 동시성 문제의 근본적인 해결 방법은 가변데이터에 순차적으로 접근할 수 있는 방법을 구상하는 것 이라고 생각했습니다
- 데이터베이스를 이용한 락
- 프레임워크 혹은 언어 단에서의 Synchronized 사용
- 혹은 자료구조..? (큐가 생각남)
[목차]
- 멀티스레드 환경에서 레이스 컨디션이 일어나는 이유
- 레이스 컨디션 해결방법
- Java synchronized - 동기화
- MySql Lock
- Pesimistic Lock
- Optimistic Lock
- Named Lock
- Redis Lock
- Lettuce Lock
- Redisson Lock
재고시스템 기본 로직
- Stock : 재고를 가지고 있는 객체
- StockRepoisoty : jpaRepository
- StockService : 재고 감소 로직
강의에서는 간단한 재고감소 로직을 동시성문제를 다룹니다.
코드는 🙆🏼♂️깃허브에 있습니다!
🖥 Stock.class
재고를 가지고있는 Stock 객체
@Entity
@Getter
@NoArgsConstructor
public class Stock {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long productId;
private Long quantity;
@Version
private Long version;
public Stock(final Long id, final Long quantity) {
this.id = id;
this.quantity = quantity;
}
public void decrease(final Long quantity) {
if (this.quantity - quantity < 0) {
throw new RuntimeException("재고 부족");
}
this.quantity = this.quantity - quantity;
}
}
🖥 StockService.class
재고를 감소 비지니스로직을가지는 Servcie 레이어
@Service
@RequiredArgsConstructor
public class StockService {
private final StockRepository stockRepository;
/**
* 재고 감소
*/
@Transactional
public synchronized void decrease(final Long id, final Long quantity) {
Stock stock = stockRepository.findById(id).orElseThrow();
stock.decrease(quantity);
stockRepository.saveAndFlush(stock);
}
}
1. 동시성 문제
- 🔝 위의 예제 코드는, 흔히 생각되는 단순 재고 감소 로직입니다. 실행 시 아무런 문제가 없지만
- ⬇️ 아래와 같은 상황에서는 예상과는 다른 결과값을 도출합니다.
🖥 StockServiceTest.class
멀티 스레드 환경을 구현한 Service Test
📌 ExecutorService
- ExecutorService란, 병렬 작업 시 여러 개의 작업을 효율적으로 처리하기 위해 제공되는 JAVA 라이브러입니다.
- ExecutorService는 손쉽게 ThreadPool을 구성하고 Task를 실행하고 관리할 수 있는 역할을 합니다.
- Executors 를 사용하여 ExecutorService 객체를 생성하며, 쓰레드 풀의 개수 및 종류를 지정할 수 있는 메소드를 제공합니다.
📌 CountDownLatch
- CountDownLatch란, 어떤 스레드가 다른 쓰레드에서 작업이 완료될 때 가지 기다릴 수 있도록 해주는 클래스입니다.
- CountDownLatch 를 이용하여, 멀티스레드가 100번 작업이 모두 완료한 후, 테스트를 하도록 기다리게 합니다.
- CountDownLatch 작동원리
- new CountDownLatch(5); 를 이용해 Latch할 갯수를 지정합니다.
- countDown()을 호출하면 Latch의 숫자가 1개씩 감소합니다.
- await() 은 Latch의 숫자가 0이 될 때 까지 기다리는 코드입니다.
@Test
public void 동시에_100개의_요청() throws InterruptedException {
int threadCount = 100;
//멀티스레드 이용 ExecutorService : 비동기를 단순하게 처리할 수 있또록 해주는 java api
ExecutorService executorService = Executors.newFixedThreadPool(32);
//다른 스레드에서 수행이 완료될 때 까지 대기할 수 있도록 도와주는 API - 요청이 끝날때 까지 기다림
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
executorService.submit(() -> {
try {
stockService.decrease(1L, 1L);
}
finally {
latch.countDown();
}
}
);
}
latch.await();
Stock stock = stockRepository.findById(1L).orElseThrow();
//100 - (1*100) = 0
assertThat(stock.getQuantity()).isEqualTo(0L);
}
👏🏻 테스트 실패
- CountDownLatch를 이용하여, 멀티스레드 작업이 100번의 재고감소 로직을 호출한 뒤에 재고가 0이 되는지 확인했지만, 실제로는 의외의 값이 나옵니다.
- 그 이유는 레이스 컨디션(Race Condition) 이 일어나기 때문입니다.
- 🔥 레이스 컨디션이란, 2 이상의 스레드가 공유 데이터에 액세스 할 수 있고, 동시에 변경하려할 떄 발생할 수 있는 문제
2. 멀티스레드로 처리할 때 레이스 컨디션이 일어나는 이유
📗 예상 작업 순서
- 우리가 멀티스레드로 작업을 할 때, 예상한건 아래의 그림처럼 순차적으로 데이터에 접근해 처리하고,
- 처리된 데이터를 다음 스레드가 접근하여 처리하는 그림입니다.
📕 실제 작업 순서
- 하지만, 실제로는 다릅니다.
- 아래의 그림처런, 같은 데이터를 동시에 변경 (공유된 가변 데이터) 하려 하기 때문에 작업 중 하나가 누락되게 될 수 있습니다.
3. 레이스 컨디션 해결 방법
- Race Condition 을 해결하기 위해서는,
- 👏🏻 하나의 스레드가 작업을 완료한 후에, 다른 스레드가 공유된 자원에 접근 가능하도록 해야 합니다.
Race Condition을 해결하는 여러 방법들
1) Synchronized 이용
- Synchronized를 메소드에 명시해주면 하나의 스레드만 접근이 가능합니다.
- 멀티스레드 환경에서 스레드간 데이터 동기화를 시켜주기 위해서 자바에서 제공하는 키워드 입니다.
- 공유되는 데이터의 Thread-safe를 하기 위해, synchronized 로 스레드간 동기화를 시켜 thread-safe 하게 만들어줍니다.
- 📌 자바에서 지원하는 synchronized는, 현제 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터 접근을 막아 순차적으로 데이터에 접근할 수 있도록 해줍니다.
(근데 강의에서는 @Transaction이 붙지 않아야만 성공함?!?!)
/**
* 재고 감소
*/
@Transactional
public synchronized void decrease(final Long id, final Long quantity) {
Stock stock = stockRepository.findById(id).orElseThrow();
stock.decrease(quantity);
stockRepository.saveAndFlush(stock);
}
테스트코드를 수정하지 않고, 로직도 변경하지 않은상태임에도 테스트가 통과되는 모습을 볼 수 있었습니다.
JAVA Sychronized 의 문제점
- 자바의 Sychronized는 하나의 프로세스 안에서만 보장이 됩니다.
- 📌 즉, 서버가 1대일때는 문제가 없지만 서버가 2대 이상일 경우 데이터에 대한 접근을 막을 수가 없습니다.
서버가 여러대 일 경우, Synchronized 는 각 프로세스의 동시접근 제어만을 보장해주기 때문에
다른 서버에서 가변 공유데이터에 접근하는 것을 막을 수 가 없어, 업데이트 도중 값이 변경될 수 있는 문제점이 여전히 남아 있습니다.
2) DataBase 이용하기
Mysql 을 활용하는 방법 (Lock)
- 2번째 방법은 DataBase Lock 을 이용해 순차적인 접근으로 제어하는 방법입니다.
1. Pessimistic Lock
- 실제로 데이터에 Lock을 걸어서 정합성을 맞추는 방법입니다.
- exclusive lock(베타적 잠금) 을 걸게되면 다른 트랜잭션에서는 lock 이 해제되기전에 데이터를 가져갈 수 없게됩니다.
- ✨ 자원 요청에 따른 동시성문제가 발생할 것이라고 예상하고 락을 걸어버리는 비관적 락 방식입니다.
- 하지만, 데드락이 걸릴 수 있기 때문에 주의하여 사용해야합니다.
예를들어 Server 1 DB 데이터를 가져올 떄, Lock 을 걸어버리면, 다른 서버에서는 Server1의 작업이 끝나 락이 풀릴 때 까지, 데이터에 접근하지 못하게 됩니다.
📌 결국 Pesimistic Lock이란, 데이터에는 락을 가진 스레드만 접근이 가능하도록 제어하는 방법입니다.
2. Optimisitc Lock
- 실제로 Lock 을 이용하지 않고 버전을 이용함으로써 정합성을 맞추는 방법입니다.
- 먼저 데이터를 읽은 후에 update 를 수행할 떄 현재 내가 읽은 버전이 맞는지 확인하며 업데이트 합니다.
- ✨ 자원에 락을 걸어서 선점하지 않고, 동시성 문제가 발생하면 그때가서 처리하는 낙관적 락 방식입니다.
- 내가 읽은 버전에서 수정사항이 생겼을 경우에는 application에서 다시 읽은 후에 작업을 수행하는 롤백 작업을 수행해야 합니다.
[과정]
1) 서버 1이 version1 임을 조건절에 명시하면서 업데이트 쿼리를 날립니다.
2) version1 쿼리가 업데이트 되어서, 디비는 version 2가 됩니다.
3) server2 가 version1 로 업데이트 쿼리를 날리면 버전이 맞지않아 실패합니다.
4) 쿼리가 실패하면 server2 에서 다시 조회하여 버전을 맞춘 후 업데이트 쿼리를 날리는 과정을 거칩니다.
3. Named Lock
- 이름을 가진 metadata locking 입니다. 이름을 가진 lock 을 획득한 후 해제할 때 까지 다른 세션은 이 lock 을 획득할 수 없도록 합니다.
- 주의할 점으로는 transaction 이 종료될 떄 lock 이 자동으로 해제되지 않습니다.
- 별도의 명령어로 해제를 수행해주거나 선점시간이 끝나야 해제됩니다.
📌 Named Lock은 Passimistic Lock 과 유사하지만, Passimistic Lock 은 row 나 table 단위로 락을 걸지만, Named Lock 은 metadata 단위로 락을 건다는 차이점이 존재합니다.
1. Pessimistic Lock 사용 하기
- 실제 데이터베이스에 락을 걸어 정합성을 맞추는 방법
- [exclusive lock 시 데이터 점유 과정]
🖥 StockRepository interface
- Repository 에 데이터 접근시 락을 거는 쿼리를 작성합니다.
public interface StockRepository extends JpaRepository<Stock, Long> {
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
@Query("select s from Stock s where s.id = :id")
Stock findByWithPessimisticLock(final Long id);
}
📙 Pessimistic Lock 의 장점
- 충돌이 빈번하게 일어난다면 롤백의 횟수를 줄일 수 있기 때문에, Optimistic Lock 보다는 성능이 좋을 수 있습니다.
- 비관적 락을 통해 데이터를 제어하기 때문에 데이터 정합성을 어느정도 보장할 수 있습니다.
📘 Pessimistic Lock 의 단점
- 데이터 자체에 별도의 락을 잡기때문에 동시성이 떨어져 성능저하가 발생할 수 있습니다.
- 특히 읽기가 많이 이루어지는 데이터베이스의 경우에는 손해가 더 크다고 합니다.
- 서로 자원이 필요한 경우, 락이 걸려있으므로 데드락이 일어날 가능성이 있습니다.
2. Optimistic Lock 사용 하기
- Optimistic Lock 은 실제 락을 사용하지 않고, 버전을 이용해서 락과 유사한 과정을 가지는 논리적인 락이라고 생각이 듭니다.
[Optimistic lock 시 데이터 점유 과정]
🖥Stock.class
- 버전 컬럼을 추가해야 합니다.
Entity
@Getter
@NoArgsConstructor
public class Stock {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long productId;
private Long quantity;
//버전 칼럼 추가
@Version
private Long version;
//로직 생략
}
🖥 StockRepository
- 버전을 확인하며 데이터를 처리합니다.
public interface StockRepository extends JpaRepository<Stock, Long> {
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
@Query("select s from Stock s where s.id = :id")
Stock findByWithPessimisticLock(final Long id);
//Optimistic Lock
@Lock(value = LockModeType.OPTIMISTIC)
@Query("select s from Stock s where s.id = :id")
Stock findByWithOptimisticLock(final Long id);
}
📙 Optimistic Lock 의 장점
- 충돌이 안난다는 가정하에, 별도의 락을 잡지 않으므로 Pessimistic Lock 보다는 성능적 이점을 가집니다.
📘Optimistic Lock 의 단점
- 업데이트가 실패했을 떄, 재시도 로직을 개발자가 직접 작성해 주어야 합니다.
- 충돌이 빈번하게 일어나거나 예상이되면, 롤백처리를 해주어야하기 때문에 Pessimistic Lock 이 더 성능이 좋을 수도 있습니다.
3. Named Lock 사용 하기
- Named Lock은 이름을 가진 metadata Lock 입니다.
- 이름을 가진 락을 획득한 후, 해지될때 까지 다른 세션은 이 락을 획득할 수 없게 됩니다.
- 주의할 점은, 트랜잭션이 종료될 떄 락이 자동으로 해지되지 않기 떄문에, 별도로 해지해주거나 선점시간이 끝나야 해지됩니다.
- Mysql 에서는 getLock( ) 을 통해 획들 / releaseLock() 으로 해지 할 수 있습니다.
[Named lock 시 Lock 점유 과정]
- Named Lock은 Stock에 락을 걸지 않고, 별도의 공간에 락을 겁니다.
- session-1 이 1이라는 이름으로 락을 건다면, session 1 이 1을 해지한 후에 락을 얻을 수 있습니다.
⚡️ Named Lock 사용시 주의사항
- 예제에서는 동일한 DataSource 를 사용하지만, 실제 서비스에서는 커넥션풀이 부족해질 수 있기에 DataSoruce 를 분리하는 걸 추천한다고 합니다.
🖥LockRepository
- 예제에서는 편의성을 위해서 Stock 엔티티를 사용하지만, 실무에서는 별도의 JDBC 를 사용해야 한다고 합니다.
public interface LockRepository extends JpaRepository<Stock, Long> {
@Query(value = "select get_lock(:key, 3000)", nativeQuery = true)
void getLock(String key);
@Query(value = "select release_lock(:key, key)", nativeQuery = true)
void releaseLock(String key);
}
🖥 NamedLockFacde
- StockService 는 부모의 트랜잭션과 별도로 실행되어야하기 때문에 propergation을 별도로 생성해줍니다
- 부모의 트랜잭션과 동일한 범위로 묶인다면 Synchronized 와 같은 문제인 DataBase에 commit 되기전에 락이 풀리는 현상이 발생합니다.
- 그렇기 때문에 별도의 트랜잭션으로 분리해서 DataBase에 정상적으로 Commit이 된 후에 락을 해제해 주려는 의도르 품고있다고합니다.
- 핵심은 Lock을 해제하기전에 DataBase에 Commit이 되도록 하는것..!!
@Component
@RequiredArgsConstructor
public class NamedLockFacade {
private final LockRepository lockRepository;
private final StockService stockService;
//부모의 트랜잭션과 별도로 실행되어야 함
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void decrease(final Long id, final Long quantity) {
try {
lockRepository.getLock(id.toString());
stockService.decrease(id, quantity);
}finally {
//락의 해제
lockRepository.releaseLock(id.toString());
}
}
}
락을 획득한 후, 비지니스 로직을 처리합니다.
그 후 finally 에서 락을 해지해줍니다.
그리구 예제에서는 같은 DataSource 를 사용해주어야하기 때문에 커넥션 풀 수를 늘려주어야 합니다.
spring:
jpa:
hibernate:
ddl-auto: create
show-sql: true
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:tcp://localhost/~/test
username: sa
password:
hikari:
maximum-pool-size: 40
🖥 NamedFacadeTest
@SpringBootTest
class NamedLockFacadeTest {
@Autowired
private NamedLockFacade stockFacade;
@Autowired
private StockRepository stockRepository;
@BeforeEach
public void before() {
Stock stock = new Stock(1L, 100L);
stockRepository.saveAndFlush(stock);
}
@AfterEach
public void after() {
stockRepository.deleteAll();
}
@Test
@DisplayName("Pessimistic LOCK 동시에_100개의_요청")
public void Pessimistic_requests_100_AtTheSameTime() throws InterruptedException {
int threadCount = 100;
//멀티스레드 이용 ExecutorService : 비동기를 단순하게 처리할 수 있또록 해주는 java api
ExecutorService executorService = Executors.newFixedThreadPool(32);
//다른 스레드에서 수행이 완료될 때 까지 대기할 수 있도록 도와주는 API - 요청이 끝날때 까지 기다림
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
executorService.submit(() -> {
try {
stockFacade.decrease(1L, 1L);
} finally {
latch.countDown();
}
}
);
}
latch.await();
Stock stock = stockRepository.findById(1L).orElseThrow();
//100 - (1*100) = 0
assertThat(stock.getQuantity()).isEqualTo(0L);
}
}
📙 Named Lock 이 장점
- 📌 NamedLock 은 주로 분산락을 구현할 때 사용합니다.
- Pessimistic 락은 time out을 구현하기 굉장히 힘들지만, Named Lock은 비교적 손쉽게 구현할 수 있다고 합니다.
- 그 외에, 데이터 정합성을 맞춰야하는 경우에도 사용할 수 있다고 합니다.
📘 Named Lock 이 단점
- 하지만., Naemd Lock 은 트랜잭션 종료 시에, 락 해제와 세션관리를 잘 해주어야하므로 주의해서 사용주어야 합니다.
- 또 실제 사용할 때는 구현방법이, 복잡할 수 있습니다.
4. Redis 이용해보기
- Redis 를 사용하여 동시성 문제를 해결하는 대표적인 라이브러리 2가지가 존재합니다.
- Lettuce
- Redisson
1. Lettuce
- Setnx 명령어를 활용하여 분산락을 구현 (Set if not Exist - key:value를 Set 할 떄. 기존의 값이 없을 때만 Set 하는 명령어)
- Setnx 는 Spin Lock방식이므로 retry 로직을 개발자가 작성해 주어야합니다.
- Spin Lock 이란, Lock 을 획득하려는 스레드가 Lock을 획득할 수 있는지 확인하면서 반복적으로 시도하는 방법입니다.
📌 Spin Lock 과정
2. Redisson
- Pub-sub 기반으로 Lock 구현 제공
- Pub-Sub 방식이란, 채널을 하나 만들고, 락을 점유중인 스레드가, 락을 해제했음을, 대기중인 스레드에게 알려주면 대기중인 스레드가 락 점유를 시도하는 방식입니다.
- 이 방식은, Lettuce와 다르게 대부분 별도의 Retry 방식을 작성하지 않아도 됩니다.
📌 Pub-Sub 과정
Redis 환경 설정
1) 먼저 도커로 레디스를 다운받아 실행해줍니다.
- redis 이미지 다운로드 : docker pull redis
- redis 실행 : docker run --name myredis -d -p 6379:6379 redis
- 실행 확인 명령어 : docker ps
2) Spring Gradle Redis 의존성을 추가합니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//redis 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}
✅ Lettuce 사용하기
- Lettuce를 작성하여 재고감소 로직 작성하기
1) setnx 명령어 사용해보기
먼저, redis cli를 이용하여 setnx 명령어를 사용해보겠습니다.
- 먼저 컨테이너 id 를 사용하여 cli 로 접속합니다.
2. setnx 명령어는 key 와 value 로 삽입합니다.
- setnx (key) (value)
- 1 이라는 key 로 맨처음 삽입할때는 성공하지만, 그 이후로는 실패합니다.
- del (key) 명렁어로 해당 key의 데이터를 삭제하고 다시 삽입하면 성공합니다.
📌 Lettuce를 활용하는 방법은 Mysql 의 NamedLock 과 유사하지만, Redis 를 활용한다는 점, 세션관리에 신경쓰지 않아도 된다는 차이점이 있습니다. (왜 세션관리 안해두됨????_??? )
2) Spring 에서 Redis Lettuce 방식 사용
🖥 RedisLockRepository
- key 를 이용한 Lock 과 unLock 메소드 정의
@Component
@RequiredArgsConstructor
public class RedisLockRepository {
private final RedisTemplate<String, String> redisTemplate;
public Boolean lock(final Long key) {
return redisTemplate
.opsForValue()
//setnx 명령어 사용 - key(key) value("lock")
.setIfAbsent(generateKey(key), "lock", Duration.ofMillis(3_000));
}
public Boolean unlock(final Long key) {
return redisTemplate.delete(generateKey(key));
}
private String generateKey(final Long key) {
return key.toString();
}
}
- SpinLock 방식으로 락을 얻기를 시도하고,
- 락을 얻은 후, 재고 감소 비지니스 로직을 처리합니다.
- 그 후, 락을 해제해주는 방식이 Lettuce 방식입니다.
@Component
@RequiredArgsConstructor
public class LettuceLockStockFacade {
private final RedisLockRepository redisLockRepository;
private final StockService stockService;
public void decrease(final Long key, final Long quantity) throws InterruptedException {
// Lock 획득 시도
while (!redisLockRepository.lock(key)) {
//SpinLock 방식이 redis 에게 주는 부하를 줄여주기위한 sleep
Thread.sleep(100);
}
//lock 획득 성공시
try{
stockService.decrease(key,quantity);
}finally {
//락 해제
redisLockRepository.unlock(key);
}
}
}
📌Sprin Lock 방식이, Lock 을 얻을 떄까지 Lock 얻기를 시도하기 떄문에, 계속해서 Redis 에 접근해서 Redis에 부하를 줄 수 있다는 단점이 존재합니다.
✅ Redisson 사용하기
- Redisson 를 작성하여 재고감소 로직 작성하기
redisson 의존성 추가
dependencies {
implementation 'org.redisson:redisson-spring-boot-starter:3.17.6'
}
1) Pub-sub 사용해보기
- pub-sub 방식을 사용해보기 위해서는 2개의 redis cli가 필요합니다.
1. (1번 cli) subscribe 명령어를 사용하여 ch1 을 구독합니다.
subscribe ch1
2. (2번 Cli) publish 명령어를 사용하여 메세지를 전달합니다.
publish ch1 hello
3. (1번 cli) 1번 은 ch1 을 구독하고 있기 때문에, 메세지가 옴을 확인할 수 있습니다.
✨ Lettuce 와 다르게 Redisson 은 계속 락 획득을 계속 시도하는게 아니기 때문에 Redis의 부하를 줄일 수 있습니다.
Redisson 같은 경우 Lock 과 관련된 클래스를제공해 줍니다.
🖥 RedissonLockStockFacade
@Component
@RequiredArgsConstructor
public class RedissonLockStockFacade {
private final RedissonClient redissonClient;
private final StockService stockService;
public void decrease(final Long key, final Long quantity) {
//key 로 Lock 객체 가져옴
RLock lock = redissonClient.getLock(key.toString());
try {
//획득시도 시간, 락 점유 시간
boolean available = lock.tryLock(5, 1, TimeUnit.SECONDS);
if (!available) {
System.out.println("lock 획득 실패");
return;
}
stockService.decrease(key, quantity);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
👏🏻 Lettuce vs Redisson
Lettuce
- 구현이 간단하다
- Spring data redis를 이용하면 lettuce가 기본이기 떄문에 별도의 라이브러리를 사용하지 않아도 된다.
- Spin Lock 방식이기 때문에 동시에 많은 스레드가 lock 획득 대기 상태라면 redis에 부하가 갈 수 있다.
Redisson
- 락 획득 재시도를 기본으로 제공한다.
- pub-sub 방식으로 구현이 되어있기 때문에 lettuce 와 비교했을 때 redis에 부하가 덜 간다.
- 별도의 라이브러리를 사용해야한다.
- lock을 라이브러리 차원에서 제공해주기 때문에 사용법을 공부해야 한다.
강의 정리 끝!!
- 📌 Lettuce를 활용한다면 세션관리에 신경쓰지 않아도 되는 이유를 잘 모르겠습니다 ㅠ
- 📌 그 외에, 방법에서는 결국 동시성 문제의 근본적인 해결방안은 데이터에 순차적인 접근으로 통제함으로써 동기화를 시켜주는게 초점이다라는 것을 느낄 수 있었던 강의였습니다 😁
참고
'🔥 공대생은 성장 중 > 강의' 카테고리의 다른 글
AWS 네트워킹 입문 (2) - 기본 네트워킹 개념 (OSI 7계층, IP 주소, 서브넷, 서브넷 마스터, 라우킹과 라우터, TCP, UDP) (0) | 2023.05.31 |
---|---|
AWS 네트워킹 입문 (1) - 클라우드 컴퓨팅이란 (0) | 2023.05.11 |
THE RED 백명석, 최범균 - 백발의 개발자를 꿈꾸며 : 코드리뷰, 레거시와 TDD : 강의 회고 및 개인 요약 정리(2) (0) | 2022.06.28 |
THE RED 백명석, 최범균 - 백발의 개발자를 꿈꾸며 : 코드리뷰, 레거시와 TDD : 강의 회고 및 개인 요약 정리(1) (0) | 2022.06.28 |