데이터 관리 삐끗하면 MSA 망함. (2)
Kafka를 활용한 CQRS 패턴, MSA 데이터베이스 동기화
CQRS(Command Query Responseibility Segregation)패턴
CQRS (Command Query Responsibility Segregation)는 데이터의 조회(Query)와 업데이트(Command)에 대한 책임을 명확히 분리하는 아키텍처 패턴입니다.
이 구조는 복잡한 비즈니스 애플리케이션의 성능, 유지보수성, 그리고 확장성을 극대화하기 위해 등장했는데요. 본질적으로, CQRS는 데이터베이스의 읽기(Read)와 쓰기(Write) 작업을 분리하는 전략을 채택함으로써, 효율적인 데이터 처리와 관리를 가능하게 해줍니다.
이 패턴을 적용함으로써, 단순히 데이터를 조회하는 데에 최적화된 데이터베이스와, 생성(Create), 갱신(Update), 삭제(Delete) 작업을 수행하는 데이터베이스를 별도로 운영할 수 있게 됩니다.
이는 쿼리 수행 시 CUD 작업에 따른 락(lock) 경쟁을 최소화하며 성능상의 이점을 제공합니다. 추가적으로, 조회 전용 데이터베이스는 스키마 재구성, 비정규화, 인덱싱 최적화 등을 통해 읽기 성능을 극대화할 수 있으며, 이는 업데이트 작업을 처리하는 서비스의 부하도 상당히 줄여줍니다.
CQRS In MSA
데이터베이스를 독립적으로 운영하고, 서비스의 책임을 분리한다는 이 사실. 굉장히 익숙하게 느껴지시지 않나요. 맞습니다. MSA와 짝짜꿍이 좋은 설계 패턴입니다. 예시를 만들어 볼까요.
예시
게시글을 작성하는 서비스가 있다고 했을때, 다음 그림과 같이 설계했다고 가정하겠습니다.
게시글을 작성하고, 수정하고, 삭제하는 서비스인 post-command-service와 그에 연결된 post-command 데이터베이스가 있구요.
게시글을 조회하는 서비스인 post-query-service와 그에 연결된 post-query 데이터베이스가 있습니다.
참고로 두 데이터베이스의 테이블 구성은 같지만, 경우에따라 query(response)에 적합한 스키마와 데이터 타입을 설정할 수 있습니다. 또, 추가로 인덱싱이나 캐싱을 세팅할 수 있겠죠.
그럼 이제 데이터베이스 싱크를 맞추기 위한 프로세스를 설계 해보겠습니다.
프로세스는 다음과 같습니다.
post-command-service가 사용자의 요청을 받아post-command데이터베이스에 CUD(Create, Update, Delete) 등의 쓰기 작업을 수행합니다.post-command-service는 작업이 발생한 내용을 이벤트로 발행해 Kafka에 전송합니다.post-query-service는 해당 토픽을 구독하고 있다가 컨슘합니다.post-query-service는 컨슘한 이벤트를 통해post-query데이터베이스에 CUD(Create, Update, Delete) 등의 쓰기 작업을 수행하여 싱크를 맞춥니다.- 사용자는
post-query-service를 통해post-query데이터베이스의 데이터를 읽습니다.
프로세스를 보다보면 자연스레 생기는 의문점들이 있습니다.
post-query-sevice는 읽기작업만 한다고 해놓고, 결국엔 쓰기작업도 해야되네?- 데이터베이스 동기화 과정중에 실패하면 어떡하지?
- 카프카가 제대로 데이터 스트리밍을 못하면 어떡하지?
등등.. 의문이 생기실텐데요. 첫번째 의문점에 대한 제 생각은 예스입니다. ‘서버 부하 경감’ 관점과 CQRS 패턴은 큰 상관관계에 있는것은 아닙니다. CQRS 패턴의 목적을 다시 정리하면서 생각해보겠습니다.
성능 최적화
- 읽기 작업: 읽기 모델은 조회 성능을 최적화하기 위해 구조화될 수 있습니다. 예를 들어, 자주 사용되는 쿼리에 맞춰 데이터를 집계하거나 인덱싱할 수 있습니다.
- 쓰기 작업: 쓰기 모델은 트랜잭션 무결성과 도메인 이벤트 처리에 초점을 맞출 수 있습니다. 이는 복잡한 비즈니스 로직을 처리하는 데 도움이 됩니다.
확장성 향상
- 명령과 쿼리 요구 사항이 다르기 때문에, 각각의 서비스를 독립적으로 확장할 수 있습니다. 이는 특히 대규모 시스템에서 효과적입니다.
유연성과 유지보수성
- 서비스가 명확하게 분리되어 있어 각 컴포넌트를 독립적으로 개발하고 유지보수할 수 있습니다. 이는 또한 기능을 업데이트하거나 수정할 때 발생할 수 있는 리스크를 감소시킵니다.
CQRS는 직접적으로 서버 부하 감소를 목적으로 하는 것은 아니지만, 읽기와 쓰기 작업의 효율적 분리를 통해 간접적으로 부하를 분산시킬 수 있습니다. 예를 들어, 읽기 모델을 별도로 스케일링하여 쿼리 요청에 더 많은 리소스를 할당하고, 쓰기 모델은 비즈니스 로직 처리에 집중할 수 있습니다. 이는 특히 읽기가 많은 애플리케이션에서 읽기 성능을 크게 향상시킬 수 있습니다.
첫번째 의문점에 대하여 더 좋은 생각이나 관점, 해결책이 있으시다면 댓글 부탁드립니다용
2,3번째 의문에 관한 것은 Kafka를 통해 해결책을 제시할 수 있습니다. 다음 포스팅 때, Spring Cloud와 EDA, CQRS 패턴에서 Kafka를 어떻게 사용하는지 코드와 함께 포스팅 하도록 하겠습니다.