티스토리 뷰
상품 주문하기를 개발하던 중,
단 1초의 오차도 없이 동시다발적으로 상품을 주문한다고 가정할 때 문제가 발생한다.
일단 동시적으로 값을 보내주기 위해 테스트를 만들어보자.
#상황
원래는 로그인 환경에서 해야하지만, 그러면 테스트가 더 복잡해지니 시큐리티에서 로그인 안해도 되도록 설정을 바꿔준 후 로컬에서 테스트를 진행했다.
포스트 맨에서 Body를 작성한 후에, 옆에 </> 누르면 자동으로 curl 코드를 만들어준다. 그대로 복사.
메모장에 여러번 복붙해서 테스트 케이스를 만들자, 여러개 붙일 때에는 & 를 붙여준다.
그대로 전부 복사해서 터미널에 실행시키면 끝
📌 문제 상황
요청을 7개를 보냈으나, 응답이 7개는 정상적으로 온다.
그렇지만, DB에는 1개밖에 처리되지 않은 상태.
어찌보면 예외 발생보다 더 최악인 상황이다
💡 그래서 주문할 때 주문을 처리하는동안 락 처리를 해주어, 다른 주문들이 접근을 못하도록 막는 것이다.
이런 상황을 대비해 스프링에서는 @Lock 이라는 어노테이션을 제공한다.
🌟 Lock 이란?
Lock이란 트랜잭션 처리의 순차성을 보장하기 위한 방법.
공유 락 (Shared Lock) : 데이터를 읽을 때 사용되어지는 Lock으로, 공유 Lock은 공유 Lock끼리는 동시에 접근이 가능함.
즉 하나의 데이터를 읽는 것은 여러 사용자가 동시에 가능함.
베타 락 (Exclusive Lock) : 데이터를 변경하고자 할 때 사용되며, 트랜잭션이 완료될 때까지 유지된다.
베타 락이 해제될 때까지 다른 트랜잭션은 해당 리소스에 접근이 불가능.
🌟 JPA 에서 제공하는 @Lock 어노테이션 종류
📌 비관적 락(Pessimistic Lock)
트랜잭션 충돌이 발생한다고 가정하고 락을 우선 락을 걸고보는 방식.
데이터베이스에서 제공하는 락 기능을 사용.
데이터를 수정시, 즉시 트랜잭션 충돌을 감지할 수 있다.
📌 낙관적 락(Optimistic Lock)
트랜잭션의 충돌이 발생하지 않는다고 가정하에 진행하는 방식.
데이터베이스에서 제공하는 Lock을 사용하지 않고 JPA가 제공하는 버전 관리 기능을 사용.
트랜잭션을 커밋하기 전까지는 트랜잭션의 충돌을 알 수 없다는 단점이 있음.
출처: [인생을 코딩하다.:티스토리]
비관적 락을 사용했다. 낙관적 락은 트래내잭션의 충돌이 발생하지 않는다고 가정하에 진행하기 때문에,
다소 위험 부담이 있을 수 있다고 생각했다. 실제로 충돌이 나면 개발자가 일일이 롤백을 해주어야한다 << 오바라고 생각했다.
그래서 비관적 락을 사용하여, 충돌이 발생하지 않더라도 일단 락을 걸고보는 방식을 택했다.
🌟 비관적 락의 종류
PESSIMISTIC_READ [공유 락]
해당 리소스에 공유락을 걸어 데이터를 읽는 것은 여러 사용자가 가능하게 한다.
단, 타 트랜잭션에서 쓰기는 불가능하다.
PESSIMISTIC_WRITE [배타 락]
해당 리소스에 베타락을 걸어, 타 트랜잭션에서는 읽기와 쓰기 모두 불가능하게 한다.
처음에는 읽기는 가능하도록 PESSIMISTIC_READ [공유 락] 처리를 해주었다.
📌 데드락의 발생
처음이랑 상황이 똑같이 데이터베이스에서는 요청이 7개인 것에 비해 count 가 1개만 감소됐다.
그리고 데드락이 발생했다.
🧐 데드락 (DeadLock), 즉 교착 상태란?
데드락(교착상태)란 두개 이상의 작업이 서로 상대방의 작업이 끝나기 만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태를 가리킨다. 한정된 자원을 여러 곳에서 사용하려고 할 때 발생할 수 있다.
1. Process 1 -> Resource 1 사용
2. Process 2 -> Resource 2 사용
3. Process 1 -> Resource 2 요청
4. Process 2 -> Resource 1 요청
3, 4번 에서 서로의 작업이 끝나기를 기다리기 때문에 교착 상태에 빠지게 된다.
나의 주문 처리 로직 중 일부는 다음과 같다.
//1. 일단 해당 날짜에 주문이 가능한지 확인. -> 안되면? 예외 발생
// Lock 거는 시점
List<ProductInfoPerNight>[] allProductInfoList = isPossibleToOrder(orderRequest);
//2. 모든 주문이 유효하다면, productInfoPerNight -> 해당 날짜에 관한것들 전부 count - 1
processOrder(allProductInfoList);
1번 에서 PESSIMISTIC_READ [공유 락]을 걸게된다. 그렇게 되면 먼저 접근란 프로세스 1은 ProductInfoPerNight 테이블의 리소스에 쓰기 권한을 갖게 된다. 2번 프로세스는 같은 데이터를 원하므로, ProductInfoPerNight 테이블의 리소스에 대해 읽을 순 있으나 쓰기 권한은 없게 된다.
2번 프로세스가 읽기만 했으므로 2번 작업에 먼저 도착한다. 하지만 2번 작업은 (count - 1)이라는 update 문, 즉 쓰기 작업을 요청하므로, 2번 프로세스는 대기하게 된다.
3번 프로세스도 다음과 같은 과정을 하고, 4번도 하고, 5번도 한다.
1번 프로세스는 모든 작업이 성공적으로 마칠 수 있지만, 그 다음 요청부터는 .. 모르겠다. 나중에 디버깅 해야겠다.
아무튼 생각만 해도 에러가 안나는게 이상한 상황이다.
📌 PESSIMISTIC_WRITE [배타 락] 의 사용
1번 프로세스가 ProductInfoPerNight 테이블의 리소스에 대해 읽기, 쓰기를 모두 Lock을 걸면서,
2번, 3번, .. n번 프로세스가 순차적으로 ProductInfoPerNight 테이블의 리소스에 접근하도록 했다.
결과는 성공적. 병렬이어도 미세한 차이로 늦게 나간 프로세스들은 상품을 못 획득한다.
마치 내가 티켓팅에서 분명 좌석을 눌렀는데 결제가 안 된 이유가.. 이거구나를 알았다. ㅋ ㅋ ㅠㅠ
데이터 베이스에서도 다음과 같이 정상적으로 나간다.
📌 근데 문제가 있다.
@Lock을 걸어준 메소드에는 사용하는 곳이 2곳이다.
- 주문 처리 (위에서 처리한 곳)
- 단순 조회용
주문 처리에서는 @Lock이 필요하지만, 밑의 경우에는 필요가 없다.
그리고 비관적 락을 걸어줌으로써, 오히려 성능이 느려지는 상황이 발생 🚨
오버 로딩도 안 되는 상황
다음과 같이 @Query를 사용해서 분리해주었다.
단순 읽기할 때에는 @Lock을 걸어주지 않았다.
끝.
출처
[SPRING, DATABASE] 상품 주문 시, Pessimistic Lock으로 동시성 제어
안녕하세요. 오늘은 Pessimistic Lock으로 동시성 제어를 하는 법에 관해 글을 작성해보려 합니다. 저는 상품을 주문하는 API를 개발하고 있었습니다. 여러 사용자가 동시다발적으로 상품을 주문 할
junghyungil.tistory.com
[데이터베이스] Lock에 대해서 알아보자 - 기본편
안녕하세요. 오늘은 DataBase의 Lock에 대해서 알아보고 정리해보는 시간을 가져보도록 하겠습니다. DataBase는 데이터를 영속적으로 저장하고 있는 시스템입니다. 이런 시스템은 같은 자원(데이터)
sabarada.tistory.com
상품 주문 동시성 문제 해결하기 - DeadLock, 낙관적 락(Optimistic Lock) & 비관적 락(Pessimistic Lock)
여러명의 사용자가 동시에 상품의 주문을 요청했을때 발생하는 동시성 문제의 해결과정
velog.io
DeadLock 이란
DeadLock
hckcksrl.medium.com
'Spring boot' 카테고리의 다른 글
[문제 해결] [성능 개선] redis를 이용한 주문 재고 관리 동시성 제어 (Facade 패턴, AOP 이용) (1) | 2023.12.20 |
---|---|
[기능 개발] MySql에 QR code 이미지 저장하고, 결제 처리하기 기능 개발기 (QR code 결제) (1) | 2023.12.08 |
[성능 개선] 비동기 방식, saveAll을 활용한 2초 ▶️ 0.2초로 개선하기 (1) | 2023.11.17 |
[리팩토링 & 예외처리] 인터페이스를 이용한 enum으로 예외처리 정돈하기. (0) | 2023.11.15 |
[예외처리] @Valid message를 response에 담아 출력하는 방법. (1) | 2023.11.13 |
- Total
- Today
- Yesterday
- 그룹스터디
- 백준
- qjzl
- 패스트캠퍼스강의
- 자료구조
- 백엔드
- 채팅기능개발
- 프로젝트후기
- 카카오API
- boj
- 국비지원캠프
- 야놀자X패스트캠퍼스부트캠프
- 데이터베이스
- #국비지원취업
- 스터디후기
- 패스트캠퍼스
- 국비지원취업
- 국비지원
- springboot
- TiL
- 커리어멘토링
- 백엔드개발자
- 부트캠프
- 야놀자
- 백엔드부트캠프
- 자료구조 #스택 #큐 #덱 #선형자료구조
- 그룹스터디워크샵
- Java
- be
- 과정중간회고
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 | 31 |