-
Notifications
You must be signed in to change notification settings - Fork 1
/
Team1-Additional_task1.txt
58 lines (46 loc) · 5.65 KB
/
Team1-Additional_task1.txt
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 동시성 제어, 디비 일관성 & 무결성 제어 방법
## POST, POST_COMMENT, RESERVED_CLASSROOM 테이블의 id 속성 값은 1씩 증가되며 들어간다.
이제껏 배웠던 방법으로는 `MAX(id) + 1`과 같은 형태로 값을 채워넣어야 한다.
하지만 트랜잭션을 사용한다 하더라도 동시에 생성을 할 경우, 같은 값이 반환될 수 있다.
물론 Primary Key 이기 때문에 중복이 불가능하여 DBMS단에서 무결성을 지켜줄 것이지만,
UX를 개선하고 DBMS에 불필요한 무결성 에러를 발생시키지 않기 위해 SEQUENCE라는 기법을 사용하였다.
## 수강 신청
### 정원을 체크하기 위한 작업
수강 신청에는 수강 정원이 있습니다.
기존 스키마 구조상으로 수강 정원이 넘는지 확인하기 위해서는 `TAKE_CLASS` 집계 함수를 사용하여 레코드 개수를 확인해야 합니다.
하지만 이 경우 트랜잭션을 사용한다 하더라도, 동시에 요청이 들어올 경우 수강 정원을 넘길 수 있습니다.
그렇기 때문에 `CLASS` 테이블에 현재 수강신청한 사용자 수를 측정하는 `Cur_student_number` 속성을 추가하고, CHECK 제약 조건을 사용해 수강 정원 이하가 되도록 설정했습니다.
그리고 트리거를 사용하여 `TAKE_CLASS`에 레코드를 추가할 때(즉, 수강 신청할 때), Cur_student_number의 값을 늘리도록 했습니다.
이럴 경우, `TAKE_CLASS` 테이블에 레코드를 추가하였는데, 수강 정원을 초과하면 롤백되어 수강 정원을 초과할 수 없게 됩니다.
반대로 `TAKE_CLASS` 테이블에서 레코드를 제거할 때는(즉 수강 신청 취소), Cur_student_number의 값을 줄여주는 트리거도 추가했습니다.
## 트랜잭션과 고립 수준
수강 신청 로직은 다음과 같이 진행된다.
(1) CLASS 테이블에서 현재 수강 신청한 사용자 수와 최대 수강 신청 가능 수를 조회하는 SELECT 구문을 호출한다.
(2) 이후 현재 수 < 최대 수라면 TAKE_CLASS 테이블에 새로운 레코드를 삽입(INSERT)한다.
(3) 이때, 트리거가 발동하여 CLASS 테이블의 현재 수강 신청한 사용자 수를 +1로 업데이트한다.
(4) 이때, CHECK 제약조건을 통해 현재 수 <= 최대 수를 만족하는지 체크한다.
이 과정에서 CLASS 테이블에 대해 SELECT와 UPDATE 구문이 호출되기에 트랜잭션으로 묶었습니다.
또한 고립 수준은 READ_COMMITTED로 설정하였는데, 성능을 가장 고려한 이유입니다.
우선 Check 제약 조건으로 인해 수강 정원 제약 조건은 강제되어 SERIALIZABLE을 사용하지 않고, READ_COMMITEED만으로도 무결성 및 일관성이 지켜지게 됩니다.
수강 신청은 단기간에 굉장히 많은 트래픽이 몰리기 때문에 SERIALIZABLE을 사용할 경우 DBMS에 매우 큰 부하가 가게 됩니다.
이미 CEHCK 제약 조건으로 무결성과 일관성이 지켜지게 되는데, 불필요하게 높은 고립 조건을 사용하는 것은 좋은 설계가 아니라 생각되어 READ_COMMITEED을 선택했습니다.
다만, 누군가 수강신청 트랜잭션을 실행 도중에 실패하게 될 경우 커밋되지 않은 Dirty Read가 발생하여 수강신청이 가능하지만 수강신청을 실패할 가능성이 있기에, 트랜잭션을 사용하면서 READ_COMMITEED로 이를 방지했습니다.
또한 이는 수강 신청 취소의 과정에서도 같은 맥락으로 적용됩니다.
## 강의실 예약
### 중복 예약을 체크하기 위한 작업
강의실 예약은 8시~22시 사이의 시간을 1시간 단위로 예약이 가능하며, 동일한 시간대에는 1명만 예약이 가능합니다.
예약 시간대를 체크하기 위해 `RESERVED_CLASSROOM` 테이블에 존재하는 `start_timestamp`와 `end_timestamp` 속성을 활용하였습니다.
강의실은 각 시간대에 1명만 예약이 가능하기에 트랜잭션을 통해 동시에 예약이 들어오는 것을 방지하였습니다.
## 트랜잭션과 고립 수준
강의실 예약 로직은 다음과 같이 진행됩니다.
(1) reserved_classroom_seq Sequence를 통해 새 예약 id를 가져옵니다.
(2) 새 예약이 현재보다 이전 날짜이거나, 종료 시간이 시작 시간보다 빠르다면 오류 메시지를 반환합니다.
(3) 새 예약이 기존 예약과 중복되는지 buildingnumber, roomnumber, start_timestamp, end_timestamp를 통해 확인합니다.
(4) 중복된다면 SQL Execption을 발생 시키고, 중복되지 않는다면 RESERVED_CLASSROOM 테이블에 id와 예약정보를 새 튜플로 삽입합니다.
이 과정에서 RESERVED_CLASSROOM 테이블에 대해 SELECT와 INSERT 구문이 호출되기에 트랜잭션으로 묶게 되었습니다.
또한 고립 수준은 SERIALIZABLE로 설정하였는데, 예약 중에 RESERVED_CLASSROOM 테이블에 다른 예약이 INSERT 되는 것을 막기위함입니다.
다른 이용자가 동일한 시간대에 예약을 시도할 경우 문제가 발생할 수 있기 때문입니다.
수강 신청과 마찬가지로 강의실 예약 작업자체가 일관성과 무결성이 중요한 작업이지만, 수강 신청과는 달리 트랜잭션을 제외한 별도의 무결성 보장 장치가 없습니다.
또한, 수강 신청과 달리 동시간에 많은 요청이 몰릴 가능성도 극히 드물기에 성능상의 문제도 적다고 판단됩니다.
그렇기에 고립 수준을 SERIALIZABLE로 설정하여, 어느 사용자가 강의실 예약 로직을 처리할 때는 다른 사용자가 접근할 수 없도록 만들었습니다.
강의실 예약 취소도 동일한 맥락으로 트랜잭션을 적용하였습니다.