최범균님 강의로 DB 트랜잭션 조-쪼금 이해하기 1,2
개발자 관점에서 트랜잭션은 간단하게 여러 읽기/쓰기 를 논리적으로 하나로 묶음
- 트랜잭션 시작 - 여러 쿼리 실행 - 커밋 또는 롤백
- 모두 반영(커밋) 또는 모두 반영하지 않거나(롤백)
이런 트랜잭션이 없다면?
1.3 에서 실패할 경우, 1.2 업데이트을 취소할 수 있는 것을 구현해야 한다.
트랜잭션 범위는 커넥션 기준으로 합니다.
만약 5 에서 롤백될 경우, 4,3 의 롤백 되고, 그 뿐만 아니라 4.1 에서 롤백도 기대하게 됩니다.
그러나, 4.1 처럼 새로운 트랜잭션을 만들었다면 4.3 에서 이미 commit 되었기 때문에 롤백되지 않습니다.
여러 메소드를 호출할 때 하나의 트랜잭션으로 묶고 싶다면, 여러 메소드에서 하나의 커넥션을 사용할 수 있는 그런 방법을 뜻합니다. 트랜잭션 전파가 바로 이런 목적에 사용됩니다.
여러 메서드 호출이 한 트랜잭션에 묶이도록 하기 위해 필요합니다.
- 예 - 스프링 프레임워크의 트랜잭션 처리
- 메서드 간에 커넥션 객체를 전달하지 않아도 한 트랜잭션으로 묶어서 실행
트랜잭션에 외부연동이 있을 경우 주의해야 합니다!
첫번째 그림에서 4번에서 문제가 발생할 경우, 2,3 은 롤백에 문제가 없을 것입니다. 그러나, 두번째 그림에서는 외부 API 가 이미 성공했다면 이는 롤백될 수 없습니다.
글로벌 트랜잭션이라는 것도 있습니다.
정리하면
- 원자성에 대한 내용이였습니다
- All or Nothing
- 트랜잭션 범위 중요
- 문제가 발생했을 때 롤백해야 하는 범위
2번째
격리
같은 데이터에 동시 접근
- 동시성은 초심자가 놓치기 쉬운 문제가 발생
- 예: 당직 담당자를 최소 1명 유지해야 한다면?
경쟁 상태(Race Condition)
여러 클라이언트가 같은 데이터에 접근할 때 문제가 발생한다.
트랜잭션 격리(Isolation)
- 트랜잭션을 서로 격리해서 다른 트랜잭션이 영향을 주지 못하게 함
가장 쉬운 방법은 트랜잭션을 순서대로 실행
- 동시접근 문제 아예 없음
- 하지만 한 번에 한 개 트랜잭션만 처리하므로 성능(처리량)저하 가능
다양한 격리 수준 지원
- Read Uncommitted
- Read Committed
- Repeatable Read
- Serializable
동시성 관련 다양한 문제들을 살펴보면서 격리 수준을 이해할 수 있습니다
- 커밋되지 않은 데이터 읽기
- 커밋되지 않은 데이터 덮어쓰기
- 읽는 동안 데이터 변경 1
- 변경 유실
- 읽는 동안 데이터 변경 2
커밋되지 않은 데이터 읽기 (dirty read)
커밋되지 않은 데이터 덮어쓰기
위 두 문제를 해결하기 위해서 Read Committed 를 제공합니다.
오직 커밋된 데이터만 읽는 행위를 말합니다.
- 커밋된 데이터만 읽기
- 커밋된 값과 트랜잭션 진행 중인 값을 따로 보관
- 커밋된 데이터만 덮어쓰기
- 행 단위 잠금 사용
- 같은 데이터를 수정한 트랜잭션이 끝날 때까지 대기
- 행 단위 잠금 사용
읽는 동안 데이터 변경1 - Read skew
읽는 시점에 따라 데이터가 바뀜
처음에 A,B는 10입니다. 검은 사용자가 A를 조회할 때 10이라는 결과물을 가지고, 하얀사용자가 그 사이에 데이터를 변경하게 되면 검은 사용자가 커밋 이후에 데이터를 조회할 경우 B가 9 나오는 문제가 발생합니다.
이러한 READ SKEW 를 해결하기 위해서 Repeatable Read 가 나왔는데, 이는 트랜잭션 동안 같은 데이터를 읽게 하는 행위를 말합니다.
다시 말해, 데이터가 변경되더라도 같은 데이터를 읽을 수 있도록 하는 것을 말합니다.
읽는 시점에 해당 버전에 해당하는 값만 읽습니다.
Read Committed 와 Repeatable Read 를 하더라도, 문제가 발생할 수 있습니다.
변경한 내용이 유실할 수 있습니다. 이를 Lost Update 라고 합니다.
같은 데이터를 쓸 때 발생하는데, 예를 들어 Count를 증가하거나 위키 페이지를 수정할 경우 발생합니다.
변경 유실에 대해서 3가지 처리 방식이 있습니다.
원자적 연산 사용
명시적인 잠금
CAS(Compare And Set)
DB가 지원하는 원자적 연산 사용
동시 수정 요청에 대해 DB가 순차 처리
예) update article set readcnt = readcnt + 1 where id = 1
명시적 잠금
- 조회할 때 수정할 행을 미리 잠금
- 예: select ... for update
CAS(Compare And Set)
- 수정할 때 값이 같은지 비교
읽는 동안 데이터 변경 2
- 한 트랜잭션의 결과가 다른 트랜잭션의 퀴리 결과에 영향
- 같은 데이터를 쓰지 않지만 실제로는 경쟁 상태
- 두 트랜잭션이 서로다른 데이터를 업데이트 하니까 경쟁상태가 아닙니다. 이런 문제를 해결하기 위해서 Serializable 을 사용합니다.
Serializable
- 인덱스 잠금이나 조건 기반 잠금 등을 사용합니다.
정리하면
동시성은 초보자가 놓치기 쉬운 문제입니다.
- 동시성 문제와 격리 수준을 이해하면 문제 발생을 줄일 수 있습니다.
잠금 시간은 최소화해야 합니다.
- 잠금 시간이 길어지면 성능(처리량) 저하
동시성 문제를 다룰 때는 다음을 알면 좋습니다.
- 사용하는 DB의 기본 격리 레벨
- DB의 격리 레벨 동작 방식
mysql 은 Repeable-read
postgres 은 Read Committed
oracle 은 Read Committed
mongodb 는 Read uncommitted
'행위 돌아보기' 카테고리의 다른 글
'내 스토리 w. 최원준 클럽장' 후기 (0) | 2022.11.17 |
---|---|
[2019] DDD Lite@Spring 동영상 시청 리뷰 (0) | 2021.07.18 |
[동영상 정리]의존성을 이용해 설계 진화시키기 - 조영호 님 (0) | 2020.08.21 |
맛집 리스트 - 검색 (0) | 2019.07.28 |
함수형 사고 - [6] 전진하라. (0) | 2019.07.26 |
댓글