1. 요구사항 정리
1.1. 기능적 요구사항
•
5,000개의 호텔에 100만 개의 객실을 갖춘 호텔 체인을 위한 웹사이트 구축
•
호텔 예약시 모든 대금을 지급한다고 가정
•
사용자는 객실 예약을 취소할 수 있음
•
객실 취소를 예상하여 10% 초과 예약이 가능하도록 함
•
객실 가격이 여유에 따라 유동적으로 변경될 수 있어야 함
1.2. 비기능적 요구사항
•
높은 수준의 동시성 지원 = 성수기나 이벤트 기간일 때 고객이 많이 몰릴 수 있음
•
적절한 지연 시간 = 응답시간이 빠르면 이상적이겠으나 요청에 몇 초 지연은 괜찮다.
1.3. 규모 추정
•
5,000개의 호텔, 100만 개의 객실, 평균적으로 70%가 사용 중, 평균 3일을 투숙
•
일일 예약 건수 = 1백만 * 0.7 / 3 = 약 240,000
•
초당 예약 건수 = 240,000 / 하루에 10^5초 = 약 3
•
즉 TPS는 그렇게 높지 않다.
2. 청사진 그리기
2.1. 예약의 라이프사이클
flowchart TD 결제대기 --> 예약취소 결제대기 --> 결제완료 결제완료 --> 환불완료 결제대기 --> 승인실패
Mermaid
복사
2.2. MSA 도입
•
MSA는 각 도메인에 집중된 단위의 단독 프로덕트로 분리한 형식의 아키텍쳐다
•
공개 API 게이트웨이를 통해서 사용자는 각 서비스를 사용할 수 있다.
◦
처리율 제한장치나 인증 등을 지원하는 완전 관리형 서비스다.
•
내부 API는 VPN이나 내부 소프트웨어를 통해서만 접속 가능하다.
•
서비스간 통신은 gRPC와 같은 고성능 원격 프로시저 호출 프레임 워크를 사용하곤 한다.
3. 설계 시작
•
고가용성을 위해서 여러 지역에 데이터베이스를 분산시켜야 한다.
•
해당 도서에서는 2년 내 가용 객실 데이터를 배치로 넣어두고 사용하는 걸로 설계했다.
•
하나의 ROW값에 예약 받을 수 있는 객실 수와 현재 예약수를 저장해둔다.
◦
그리고 예약 받을 수 있는 수에 10%을 증가시켜 예약 가능 수로 책정한다.
3.1. 동시성
•
만약 같은 사용자가 예약을 여러번 누른다면?
◦
클라이언트 측에서 예약 버튼 클릭시 버튼을 비활성화 시켜서 예방 할 수 있다.
▪
자바스크립트를 아는 사용자면 우회 가능하다
◦
멱등성을 갖추도록 예약키를 생성해서 예약시 함께 첨부하도록 한다.
▪
같은 키로 예약할 시 동시에 여러번 클릭해도 처음에만 예약된다.
•
여러 사용자가 동시에 잔여객실이 1밖에 없는 같은 객실을 예약하려 한다면?
◦
레이스컨디션에 의해서 읽는 시점에 따라 둘 다 객실이 하나가 남았다고 뜰 수 있고 이 경우 오버부킹 될 수 있다.
◦
이런 문제를 해결하려면 해당 Row에 Lock을 걸어야한다.
3.1.1. 비관적 Lock
•
가장 기본적이며 간편한 락기법이며 하나의 트랜잭션이 만들어지면 작업이 끝날때까지 해당 ROW를 갱신할 수 없는 상태로 만든다.
•
구현이 쉽고, 데이터 경합이 심할때 이용하면 좋다.
•
여러 레코드에 락을 건다면 교착상태가 발생할 수 있고, 성능에 영향을 미칠 수 있다.
3.1.2. 낙관적 Lock
•
낙관적 락은 ROW에 버전이나 타임스탬프를 남겨서 같은 자원 갱신을 막는 방안이다.
◦
일반적으로 버전을 더 나은 방법으로 여긴다.
•
방법은 간단한데 ROW갱신 시 버전도 같이 업데이트 하는데 버전이 같은걸로 업데이트 하려고 할시 후속 쿼리는 오류처리하도록 하는것이다.
•
일반적으로 비관적락보다 빠르고 데이터베이스 자원에 락을 거는게 아니기 때문에 교착상태에 빠질걱정이 없다.
•
그렇지만 경쟁이 치열한 상황이라면 성능이 떨어질 수 있다.
◦
매 갱신마다 유효성 검사를 해야하기 때문이다.
3.1.3. 데이터베이스 제약조건
•
데이터베이스 갱신 시 자체적으로 연산 후 등록하도록 하는 방법이다.
•
구현이 쉬운 장점이 있다.
•
경쟁이 심하면 연산 수가 증가해 성능이 떨어질 수 있다.
3.2. 캐시
•
호텔 캐시는 현재와 미래의 데이터만 중요하다.
◦
때문에 TTL, LRU를 통한 캐시 정책이 적절하다.
•
일반적으로 잔여 객실 수를 캐시로 보여주고 이후 실제 객실은 DB로 질의하도록 한다.
3.3. 서버간 데이터 일관성
•
MSA에서는 각자 독립적인 데이터베이스를 가지기 때문에 데이터 일관성 문제를 낳는다.
•
해결 방법의 대표적 예시
◦
2단계 커밋 = 여러 노드에 걸친 원자적 트랜잭션 실행을 보증하는 프로토콜
▪
단, 블록킹 프로토콜이여서 어느 한 노드에 장애가 발생하면 복구될 때까지 중단된다.
▪
성능이 뛰어난 프로토콜은 아니다.
◦
사가 패턴
▪
각각의 트랜잭션은 완료되면 다음 트랜잭션을 시작한다.
▪
실행 중 어느 트랜잭션이 실패하면 순차적으로 이전 트랜잭션을 되돌린다.
◦
2PC는 모든 노드의 트랜잭션을 하나로, 사가는 각 단계별로 분리한 개념