-
Notifications
You must be signed in to change notification settings - Fork 2
2차: Skip Locked 기반 락 병목 해결
김현준 edited this page Sep 3, 2024
·
2 revisions
문제 상황의 ERD
- 하나의 축제(festival)은 여러 개의 티켓(ticket) 종류를 가짐 (OneToMany)
- 예를 들면 티켓도 혜택 종류에 따라 가격이 다른 티켓을 가질 수 있다는 상황을 가정
- 티켓(ticket)은 하나의 티켓 재고(ticket_stock)를 가짐 (OneToOne)
- 실질적인 티켓 재고는 remain_ticket_stock에서 int로 하나씩 차감하는 형태로 관리가 됨
- 하나의 컬럼으로 관리
- 선착순 로직 상 동시 예매가 일어날 것이 분명했고 이에 따라 낙관적 락보다는 비관적 락을 이용해 재고에 대해 Exclusive Lock을 걸어 동시성 문제를 해결했음
- 문제는 재고를 하나의 컬럼으로 관리 했기 때문에 아래의 그림과 같이 락이 걸리게 되면 그 락이 풀릴 때까지 모든 락이 기다리게 되었음
- 위의 문제로 락을 기다리는 커넥션이 늘고, 커넥션의 응답을 기다리는 쓰레드가 많아짐
- 커넥션 풀과 쓰레드 풀에서 병목 현상이 발생함
- 분산락 사용
- 데이터베이스 수준이 아닌 애플리케이션 수준에서 제어하는 방법
- 추가적인 인프라 필요, 단일 서버에서 필요 없는 네트워크 지연 overhead 발생
- DB 락 없이 자바 큐 사용해 배치 처리
- 자바 수준에서 제어 가능
- 순차적인 메시지 순서를 보장할 경우 추가 로직 필요, 추후 서버 수평 확장시 변경 가능성 높음
- MySQL SKIP LOCKED
- 데이터베이스 수준에서 제공되는 기능으로 구현이 상대적으로 간단함
- 동시성 처리 향상 및 락 대기 시간 감소
- MySQL 8.0 이상에서만 동작하는 기능으로 DB에 종속적임
따라서, 추가적인 인프라 도입 없이 서버의 DB 또한 MySQL이였기에 MySQL SKIP LOCKED을 해결 방안으로 채택
- 일반적인 경우
- 잠긴 행을 포함하는 요청 시 트랜잭션은 해당 행 잠금이 해제 될 때까지 기다림
- 행 잠김이 해제되면 그때 해당 행을 포함한 결과를 반환
- SKIP LOCKED 경우
- 잠긴 행을 포함하는 요청 시 트랜잭션은 행 잠금을 해제 할 때까지 기다리지 않고 건너뜀
- 잠긴 행을 제외한 결과 값을 반환
- 따라서 잠긴 행에 따라 요청 시 일관적이지 않은 결과를 보여줌
- 여러 세션이 동일한 대기열과 같은 테이블에 액세스할 때 잠금 경합을 피하기 위해 사용
따라서 아래와 같은 방식으로 동작
변경된 ERD
- 하나의 축제(festival)은 여러 개의 티켓(ticket) 종류를 가짐 (OneToMany)
- 티켓(ticket)은 여러 개의 티켓 재고(ticket_stock)를 가짐 (OneToMany)
- 티켓 재고만큼 ticket_stock에서 레코드가 생성됨
- ticket_stock_member_id엔 티켓 재고를 점유한 member_id가 들어감
- 따라서 아래의 그림과 같이 티켓 재고를 얻는 행위에서 다른 유저가 점유한 레코드의 잠금 해제를 기다리지 않고 잠금이 걸리지 않은 1개의 행을 빠르게 얻어 옴
- 결과적으로 잠금에 대한 경합이 일어나지 않기 때문에 쓰레드와 커넥션에 대한 병목이 감소할 것으로 보였음
- 3000, 5000명 기준
- 상세 페이지 조회
- 티켓 목록 조회
- 티켓 결제 가능 여부 조회
- 결제 정보 미리보기 페이지 조회
- 티켓 결제
https://dev.mysql.com/doc/refman/8.4/en/innodb-locking-reads.html