데이터의 일관성을 유지하는 방법에는 여러가지가 있다. 그 중 분산 트랜잭션과 CQRS에 대해 알아보자.
분산 트랜잭션
여러 독립적인 시스템등에서 데이터를 일관성 있게 처리하기 위해 하나의 트랜잭션처럼 동작하도록 설계된 트랜잭션이다.
- 단일 트랜잭션이 여러 시스템에 걸쳐 발생할 때, 모든 시스템이 해당 트랜잭션을 성공적으로 완료하거나, 모든 시스템이 트랜잭션을 실패로 처리하도록 보장한다.
+) 트랜잭션
트랜잭션은 데이터베이스의 상태를 변환하는 작업 단위, ACID를 만족해야 한다.
- 원자성(Atomicity) : 트랜잭션은 전부 성공하거나 전부 실패하여, 부분적인 작업 수행이 없는 것을 보장한다.
- 일관성(Consistency) : 트랜잭션이 완료된 후에도 데이터베이스는 모든 무결성 제약 조건을 유지한다.
- 격리성(Isolation) : 동시에 실행되는 트랜잭션이 서로 간섭하지 않도록 보장한다.
- 지속성(Durability) : 트랜잭션이 성공적으로 완료된 후의 결과는 시스템 장애가 발생해도 영구적으로 유지된다.
분산 트랜잭션에서 트랜잭션을 조율하고 일관성을 보장하기 위해 분산 트랜잭션 프로토콜인 2PC나 SAGA 패턴을 사용하여 관리한다.
- 2PC(Two-Phase Commit) : 준비 단계(Prepare)와 커밋 단계(Commit)로 나누어 트랜잭션을 처리한다.
- 준비 단계 : 각 참여 노드는 트랜잭션 준비 상태를 확인하고, 준비 완료를 마스터 노드에 알린다.
- 커밋 단계 : 마스터 노드는 모든 참여 노드가 준비되었음을 확인하고, 트랜잭션을 커밋하도록 지시한다. 만약, 준비가 완료되지 않은 노드가 있다면 트랜잭션을 롤백한다.
- SAGA 패턴 : 트랜잭션을 여러 단계로 나누어 처리하고, 각 단계가 독립적으로 커밋된다. 실패 시 보상 트랜잭션을 실행하여 상태를 롤백한다.
- 주문 생성 단계 : 사용자가 주문을 생성한다.
- 결제 처리 단계 : 결제 서비스가 주문 결제를 처리한다.
- 재고 감소 단계 : 재고 서비스가 주문된 상품의 재고를 감소시킨다.
- 각 단계가 성공적으로 완료되면 다음 단계로 넘어가고, 실패하면 이전 단계에서 수행된 작업을 취소한다.
분산 트랜잭션은 여러 데이터 소스에 걸쳐 일관된 데이터 상태를 유지할 수 있다. 하지만, 복잡성이 증가하고, 2PC와 같은 프로토콜을 사용하면 트랜잭션의 준비와 커밋 단계에서 지연이 발생하는 등 네트워크 오버헤드가 증가할 수 있다는 단점이 있다.
| CQRS
CQRS는 명령과 조회의 책임을 분리하는 소프트웨어 디자인 패턴이다. 이 패턴은 읽기 작업과 쓰기 작업을 서로 다른 모델로 분리하여, 각 작업에 최적화된 구조를 사용할 수 있도록 한다.
- 명령(Command) : 데이터를 변경하는 작업이다. 예를 들어, 주문 생성, 결제 처리, 계정 업데이트 등이 명령에 해당한다. 명령은 데이터베이스에 대한 쓰기 작업을 수행한다.
- 명령 모델은 데이터의 상태 변경을 담당한다. 이는 복잡한 비즈니스 로직을 포함할 수 있으며, 데이터 무결성을 보장하기 위한 트랜잭션을 사용한다.
- 조회(Query) : 데이터를 조회하는 작업이다. 예를 들어, 주문 내역 조회, 계정 정보 조회 등이 조회에 해당한다. 조회는 데이터베이스에 대한 읽기 작업을 수행한다.
- 조회 모델은 읽기 전용 데이터베이스 또는 캐시를 사용하여 빠른 응답을 제공한다. 이는 단순한 데이터 조회를 위한 최적화된 구조를 가질 수 있다.
CQRS는 읽기와 쓰기를 분리하여 각 작업에 최적화된 데이터 저장소와 인프라를 사용할 수 있다. 예를 들어, 조회 성능을 높이기 위해 읽기 전용 데이터베이스를 사용하거나, 캐시를 활용할 수 있다. 또한, 읽기 쓰기를 독립적으로 확장할 수 있고, 비즈니스 로직이 명령 모델에 집중되어 복잡한 상태 변경 로직을 관리하기 쉽다.
하지만, 명령 모델과 조회 모델을 각각 설계하고 관리해야 하므로 복잡성이 증가하고, 명령 모델과 조회 모델 간의 데이터동기화가 필요하다.
+) 읽기 요청 최적화와 쓰기 요청 최적화
시스템이 읽기 전용인지, 쓰기 및 업데이트 전용인지도 중요하다. 데이터 제공 및 저장에서 가장 많은 시간을 소모하는 부분은 DB에서 데이터를 조회하거나 쓰는 것이다.
읽기 요청 최적화
- 캐시 사용 : 자주 조회되는 데이터를 캐시에 저장하여 데이터베이스에 직접 접근하는 횟수를 줄이고 조회 속도를 높이는 방법
- 데이터베이스 사용 최적화 :
- 인덱싱 : 특정 컬럼에 인덱스를 추가하여 검색 속도를 높이는 방법
- 샤딩 : 데이터를 여러 노드로 분산하여 읽기 요청을 병렬로 처리하고 부하 분산
- 읽기 전용 데이터베이스 : 마스터 - 슬레이브 구조에서 슬레이브 데이터베이스를 읽기 전용으로 활용하여 요청 처리
- 쿼리 최적화 : SQL 쿼리를 효율적으로 작성하여 불필요한 연산을 줄이고 성능을 향상
쓰기 요청 최적화
- 비동기 처리 : 데이터를 즉시 처리하지 않고 Queue에 넣은 후, 백그라운드에서 비동기로 처리
- 배치 처리 : 여러 쓰기 요청을 모아서 한 번에 처리하여 데이터베이스에 접근하는 횟수를 줄이고 효율을 높이는 방법
- 분산 DB : 데이터를 분산된 데이터베이스 노드에 나누어 저장하여 쓰기 성능을 향상
정리하면, 읽기 요청 최적화는 읽기 성능을 높이고 데이터베이스 부하를 줄이는 데 초점이 맞추어져있고, 쓰기 요청 최적화는 쓰기 작업의 처리량을 높이고 지연 시간을 줄이는 데 초점이 맞추어져 있다.
'💻 개발 > Back-end' 카테고리의 다른 글
12/2 - TIL : Fallback (0) | 2024.12.02 |
---|---|
12/1 - TIL : 모듈이란? (0) | 2024.12.01 |
11/28 - TIL : 이벤트 소싱과 CQRS (0) | 2024.11.28 |
11/17 - TIL : Builder 패턴 (0) | 2024.11.17 |
Framework와 Library의 차이 (0) | 2024.11.09 |