| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 소프티어
- 2025 계획
- 테크쇼
- FCM
- 공룡책
- Spring
- OS
- Coputer Science
- Junit 5
- db
- 직장인 회고
- 일상
- 자바
- 인프콘2023
- Test Doulbe
- mapstruct
- MySQL
- Test
- ExceptionResolver
- 2024회고
- Test code
- Java
- enumSet
- parallelconsumer
- softeer
- proxyFactory
- Server
- modelmapper
- JPA
- Service 계층 테스트
- Today
- Total
공부내용공유
트랜잭션 1편 : 간단 복습하기 (feat: DB는 커밋했는데 커밋 메세지가 유실된다면?) 본문
트랜잭션 1편 : 간단 복습하기 (feat: DB는 커밋했는데 커밋 메세지가 유실된다면?)
forfun 2025. 5. 14. 21:51서론
트랜잭션은 개발자라면 학부생 때 데이터베이스 과목에서도 공부하고 웬만한 스프링 + sql db 강의에서도 다루기에 익숙한 개념일 것이다.
지금 하고있는 스터디에서 '데이터 중심 어플리케이션 설계'라는 책으로 스터디를 진행중인데 트랜잭션에 관련한 부분을 공부하였다.
스터디를 준비하고 진행하면서 잊었거나 몰랐던 개념들을 알게되었고 책에서 제시하는 시나리오들을 보면서 지금까지 비관적인 시나리오를 고려하며 개발하는 능력이 부족했구나를 느꼈다.
이 글은 해당 스터디를 진행하면서 내가 기억하고 싶은 부분들을 정리하기 위해 작성하였다.
본론
이 글의 목차는
- ACID 간단히 복습하기
- 우리가 한번쯤은 고민해봐야 할 상황
- DB는 커밋했는데 성공 메세지가 유실된다면?
으로 구성될 예정이다.
ACID 간단히 복습하기
관계형 데이터 베이스의 특징을 설명하라 하면 대부분이 ACID하다고 답변을 할 것이다.
ACID 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)
나도 학부생 때는 그렇구나 하고 시험을 위해 암기만 하고 넘어갔다. 그리고 실무에서는 빅테크 기업들에서도 쓰는 소프트웨어인데 알아서 잘 해주겠지 하고 그냥 사용을 하였다.
해당 책에서는 이 네가지 특성에 대해 설명을 해주는데 한번 짚고 넘어가면 좋을 것 같다.
원자성 Atomicity
ACID에서 원자성은 프로그래밍에서 사용하는 원자성이라는 개념과는 다르다. 프로그래밍에서 원자성은 멀티 스레드 상황에서 한 스레드가 원자적 연산을 실행했다면 다른 스레드는 그 연산이 끝난 결과만을 볼 수 있음(연간 중간의 결과를 볼 수 없음)을 의미한다.
반면 ACID에서 원자성은 클라이언트의 여러 쓰기 요청을 한 요청(원자적인 요청)으로 취급하는 특성을 의미한다. 즉 네트워크, 디스크 이슈로 쓰기 요청중 일부가 실패했다면 이전에 성공한 요청도 모두 되돌리는(roll back) 특성이다.
책에서도 언급하지만 원자성 이라는 단어보다는 abort ability (되돌리는 능력)이 좀 더 맞는 네이밍인 것 같다.
일관성
일관성에 관한 부분을 읽으면서 '이전에 공부할 때 정말 암기만 했구나' 라는 생각이 들었다.
책에서는 일관성을 데이터에 대해 항상 진실이어야 하는 불변식으로 설명을 한다. 불변식이 유효한 상태 (올바른 상태)에서 트랜잭션이 시작하고 트랜잭션도 유효하다면 결과도 불변식이 유효한 상태임을 확신할 수 있다는 특성이다.
하지만 이러한 특성은 DB에서 보장하는건 한계가 있다. 데이터 칼럼의 타입, pk, 외래키, 유니크 키의 일광성은 db에서 보장할 수 있다. 하지만 비즈니스상 제약 조건 (ex : 예금 통장은 금액이 마이너스가 될 수 없다.)은 db가 아닌 어플리케이션에서 져야할 책임이다.
즉 일관성을 위해서 어플리케이션이 DB에 의존하는건 맞지만 DB의 특성이 될 수는 없다.
격리성
격리성은 아까 원자성에서 언급한 프로그래밍에서의 원자성과 유사한 개념이다. 많은 글보다는 그림 한 개가 문제가 되는 상황을 잘 설명할 것이다.
이와 같은 상황이 발생할 경우 어플리케이션이 의도한 결과와 다른 결과가 나올 수 있다. 이를 위해 DB는 기본적으로 트랜잭션이 서로 격리되어 순차적으로 실행되는 것처럼 동작을 한다. 이는 성능과 트레이드 오프여서 상황에 맞는 동시성 제약 설정을 지원한다.
자세한 내용은 길어지기에 별도의 챕터에서 다룰 예정이다.
지속성
지속성은 트랜잭션이 (트랜잭션 내 모든 쓰기 요청들이) 커밋이 되었다면 관련 데이터들은 DB가 죽더라도 저장이 됨을 보장하는 특성이다.
메모리에 있던 데이터가 디스크로만 가면 지속성이 완벽히 보장된다고 할 수 있나? 그렇지 않다, 디스크가 손상을 입을경우 데이터가 유실될 수 있기 때문이다.
그렇기에 완벽한 지속성은 존재하지 않고 완벽에 가까워지기 위해 백업, 다중 노드 사용, 리전 분리등 다양한 기법들이 사용되고있다. (이러한 기법들로 인해 관리 포인트가 늘어난다는 트레이드 오프가 있다.)
우리가 한번쯤은 생각해봐야할 케이스
해당 챕터에서 책은 여러가지 일어날 수 있는 케이스들을 제시하였다. 이미 널리 알려진 케이스도 있었고 생각해보지 못한 것들도 있었다.
이런 저런 상황들을 보면서 내가 너무 낙관적으로 개발을 하고 있었구나 라는 생각이 들었다. 케이스들을 간단히 얘기하자면
- 트랜잭션이 실제로 성공했지만 성공했다는 메세지가 네트워크에서 유실되어 클라이언트가 실패했다고 인식하게된 경우
- 데이터베이스가 디스크에 값을 쓰고 있는데 그 중간에 전원이 나가는 경우
- 문서를 쓰고 있는데 다른 클라이언트가 문서를 읽는 경우
1번 같은 경우는 어플리케이션 (클라이언트)에서 고려해야할 문제이다. 다음 챕터에서 얘기할 예정이다.
2번은 데이터베이스에서 처리할 문제이다. 일반적으로 디스크 상에 쓰기 전 로그 (redo log)라는 자료구조에 변경 내용을
작성하기 때문에 데이터베이스가 고장 후 복구 될 때 올바른 데이터 복원을 도와준다.
3번의 경우에는 어플리케이션과 DB 둘 다에서 어느정도 책임져야할 문제인데 다음 글에서 집중적으로 다룰 격리 수준에서 얘기해보자.
물론 거의 발생하지 않을 상황이고 치명적이지 않은 경우에는 오류 상황에 대한 적절한 알림 시스템만 구축하고 수동 처리를 해도 크게 문제가 되지 않을 것이다. (과한 처리는 오버 엔지니어링이 될 수 있다.)
하지만 하나의 오류가 치명적인 상황도 있고 이럴 때 몰라서 넘어가는 엔지니어와 알지만 효율성을 고려하는 엔지니어의 차이가 드러날 것이다.
DB는 커밋했는데 성공 메세지가 유실된다면?
위와 같이 DB에서는 성공했지만 메세지가 유실되어서 클라이언트가 재시도를 한다면? 치명적이지 않은 데이터 생성이라면 큰 문제가 되지 않을 수 있다.
하지만 결제나 비슷하게 치명적인 도메인 관련이라면? 이는 최악의 사용자 경험과 법적 문제가 생길 수 있다. 어떤 해결 방법들이 있을까?
2가지 정도 해결 방법이 떠올라서 간단히 설명해보겠다. (잘못된 생각이거나 다른 좋은 생각이 있으시다면 댓글에 남겨주시면 감사하겠습니다.)
1. 고유 멱등성 키 생성
결제나 중요 기능의 경우 단계를 나눠서 진행하게 하고 이전 단계에서 해당 프로세스에 대한 고유하한 키를 만들어 놓는다.
그리고 다음 단계에서 해당 키를 기반으로 상태를 업데이트하거나 저장을 한다면 사용자가 실패한줄 알고 다시 시도해도 중복 결제가 되지 않을 것이다. (중복인게 확인된 경우 결제 완료라고 사용자에게 알림을 주면 된다.)
이렇게 한 프로세스에 대해 고유한 키를 만들어서 비즈니스 로직 자체를 멱등하게 만들면 예상치 못한 상황에서 사용자가 재시도를 해도 보다 더 안정적인 결과를 기대할 수 있다.
2. 커밋 실패시 처리 로직 정교화
커밋이 실패하면 해당 비즈니스 로직에서 예외 처리 로직으로 넘어가게 될 것이다. 해당 부분에서 단순히 로깅과 예외를 클라이언트에게 내려주는 것이 아닌 관련 데이터가 없는지 조회를 하고 문제가 없다면 상황에 맞는 응답을 내려줄 수 있을 것이다.
물론 이렇게 될 경우 다른 상황에서 문제가 생겨도 데이터를 조회하는 불필요함이 생긴다. (DB에 트래픽 과부하로 연결이 안될 때 추가적인 조회 요청은 더 안좋은 상황을 유발할 수 있다.)
지금 당장은 이렇게 2가지 정도 방법만 생각나지만 각 케이스마다 다양한 제약들이 있을거고 다양한 수단들이 있을 것이다. 그에 따라 최적의 방법을 선택하여 예외 상황이 발생하여도 데이터 정합성을 지켜줄 것이다.
일어날 확률이 크지 않기에 너무 과한 조치는 오버엔지니어링이 될 수 있지만 한번쯤은 고민해보고 정합성이 중요한 도메인에서는 이러한 문제를 해결할 수 있는 최소한의 방어 수단을 준비해야 한다고 생각한다.
결론
트랜잭션 관련 부분을 공부하면서 기본 개념이나 그 때는 놓쳤던 부분들을 실무와 연결하면서 생각하니 재밌고 다시 한번 기초의 중요성을 느꼈다.
이번 글에서는 '데이터 중심 어플리케이션 설계' 책의 앞부분 일부만 다뤘고 다음 글에서도 트랜잭션 관련 내용들과 내 경험 및 생각들을 엮어서 글을 작성할 예정이다.
'ComputerScience > DataBase' 카테고리의 다른 글
| Redis Persistence 알아보기 (RDB, AOF) (0) | 2024.08.24 |
|---|---|
| Redis 알아보기 (feat: redis는 single thread?) (0) | 2024.08.18 |
| MySQL GroupBy 사실과 오해 (feat: only_full_group_by, window function) (0) | 2024.05.01 |
| MongoDB 정규화, 반정규화 (0) | 2024.04.28 |
| MongoDB Bulk Ops (feat: insertMany, Bulk Write) 알아보기 (0) | 2024.04.21 |