day26

Antipiebse·2022년 4월 18일
0

TIL

목록 보기
16/17
post-thumbnail

좀 더 디테일한 결제API 만들기

지난 번 결제, 환불 api는 여러가지 에러에 대한 대처가 어렵다. 이를 해결하기 위해 acid/transaction,isolation등 여러가지를 구현해보자.


ACID/ Transaction(결제는 성공했으나 중간에 에러가 발생한 경우)

포인트를 충전하는 과정은 아래와 같다.

  1. PointTransaction 테이블에 1000원을 충전했다고 1줄 작성(INSERT)

  2. User테이블에서 철수 데이터를 가져오기(SELECT)

  3. 기존 1000원에 충전한 돈 1000원을 더한 2000원으로 업데이트(UPDATE)

이러한 과정이 모두 성공해야 돈이 업데이트가 된다.

만약 1번까지는 성공하고 2번에서부터 실패하게 되면 어떻게 될까?

결제는 됐지만 DB는 아무런 변경사항이 있지 않다. 즉, 돈만 나간다.

서비스를 만들 때 가장 큰 문제는 위와 같은 문제 등으로 발생하는 데이터의 오염이다.

이를 해결하기 위해 위와 같은 과정을 하나로 묶어서 처리하는 것이 좋다. 이처럼 여러 과정을 하나로 묶은 것을 Transaction(작업의 단위)이라고 한다.

강제로 오류를 발생시켜 돈을 충전해봐도 충전되어 있지 않은 결과를 확인할 수 있다.

mysql에서 관리하는 variable들

# 커넥션 최대값(max_connections)
show variables;

# 커넥션 최대값 조정
set global max_connections = 15;

# 현재 연결된 커넥션 갯수(Thread_connected)
show status;

# 현재 연결된 커넥션 목록
show processlist;

# 커넥션 종료(kill 커넥션번호)
kill 25;

트랜잭션의 속성들

ACID

Isolation-Level(격리성-수준)

격리성 수준을 나타낸 표.

1. Read-Uncommitted

롤백이 확실하지 않은 상태에서 커밋되지 않은 정보들을 보여주어 dirty-read(제대로 처리되지 않은 정보들을 보여줌)가 가능

2. Read-Committed

기존에 있던 가장 최신 정보들을 읽을 수 있게 해줌. 그러나non-repeadtable-read(여러 번 같은 요청을 보냈을 때 똑같은 정보를 못 보여줄 수도 있음)가 가능

3. Repeatable-Read(mysql기본)

반복 읽기를 허용하는데 phantom-read가 발생할 수 있음!!(그러나 mysql에서는 발생하지 않는다. db의 종류마다 다를 수 있음.)

4. Serializable

데이터 조회시 락을 걸고 조회하므로 다른 쿼리에서 조회 못하게 막는다(대기).

그럼 이러한 수준이 높다고 좋을까?

그건 아니다. 수준이 높을 수록 성능을 많이 요하기 때문에 반드시 좋은 것은 아니다.ㅏ'

Serializable

철수:3000, 훈이:2000, 영희: 5000원을 갖고 있을 때 철수와 훈이가 영희에게 가진 돈을 모두 주려고 한다.

이를 해결하기 위한 일반적인 순서

  1. 영희가 가진 돈 조회
  2. 철수가 가진 돈 주기
  3. 영희의 돈 업데이트
  4. 철수의 돈 업데이트
  5. 영희가 가진 돈 조회
  6. 훈이가 가진 돈 주기
  7. 영희의 돈 업데이트
  8. 훈이의 돈 업데이트

만약 철수가 영희에게 돈을 주기 위해 영희의 돈을 조회했을 때, 훈이도 영희의 돈을 동시에 조회한다면?
1. 5000+3000(철, 영)
2. 5000+2000(훈, 영)

위의 순서로 작동하면 db에는 7000원이 저장되는 경우가 발생한다.

이를 해결하기 위해 serializable을 사용하고 락을 걸어보자.

낙관적락

낙관적 락은 DB 충돌 상황을 개선할 수 있는 방법 중 2번째인 수정할 때 내가 먼저 이 값을 수정했다고 명시하여 다른 사람이 동일한 조건으로 값을 수정할 수 없게 하는 것이다. 그런데 잘 보면 이 특징은 DB에서 제공해주는 특징을 이용하는 것이 아닌 Application Level에서 잡아주는 Lock이다.

비관적락

철수가 영희에게 돈을 주기 위해 락을 걸어 훈이가 그 사이에 조회하지 못하도록 한다.

이때 사용하는 쿼리문은 for update는 비관적락을 걸어 실행되고 나선 다른 요청들이 조회하도록 한다.(ex: 영화관자리 예약 시 자리에 락을 걸어 예매를 편하게 함, 기차, 키오스크 예매)

공유락(Shared Lock)

pessimistic_read(읽기전용 쓰기잠금)

베타락(Exclusive Lock)

pessimistic_write(읽기쓰기 모두잠금)

데드락(Dead Lock)

lock이 죽은 경우!
어떤 api두 개가 있을 때 서로 정반대의 순서로 트랜잭션을 실행한다고 한다. 첫 번째는 1-2-3의 순서, 두 번째는 3-2-1의 순서로 접근한다고 가정하면 서로 2번째 코드가 구현되길 기다린다. 이를 락이 죽은 것으로 보아 dead lock이라고 표현한다. 가급적이면 동일한 순서로 작동할 수 있도록 코딩하자.

profile
백엔드 주니어 개발자

0개의 댓글