개요
QUIT 팀 프로젝트에서 Kafka를 사용한 비동기 처리를 구현했다. 프로젝트 막바지에 발표 자료를 만드는 과정에서 Kafka에 대한 에러 발생에 대한 처리를 하지 않았다는 사실을 깨달았고, 프로젝트 완료 후 리팩토링 겸 공부를 위해 TOP와 CDC에 대해 알아보려고 한다.
Transactional Outbox Pattern과 CDC의 필요성?
- MSA 구조에서 각 서비스 간 데이터 일관성을 유지하면서 이벤트를 발행하기 위해서 필요하다.
- 단일 작업에 데이터베이스 쓰기 작업과 메시지 또는 이벤트 알림이 모두 포함된 경우 분산 시스템에서 발생하는 이중 쓰기 작업 문제를 해결할 수 있다.
- 데이터베이스 업데이트 후 마이크로서비스가 이벤트 알림을 보내는 경우 데이터 일관성과 신뢰성을 보장하기 위해 두 작업이 원자적으로 실행되어야 한다.
- DB 업데이트는 성공했지만 이벤트 알림이 실패할 경우 다운스트림 서비스는 변경 사항을 인식하지 못해 시스템이 일관되지 않은 상태가 될 수 있다.
- DB 업데이트는 실패했지만 이벤트 알림이 전송되면 데이터가 손상되어 시스템의 신뢰성에 영향을 미칠 수 있다.
문제 및 고려 사항
- 중복 메시지: 이벤트 처리 서비스에서 중복된 메시지나 이벤트를 보낼 수 있으므로, 처리된 메시지를 추적하여 소비하는 서비스를 멱등 상태로 만드는 것이 좋다.
- 알림 순서: 서비스에서 DB 업데이트 순서와 동일한 순서로 메시지나 이벤트를 전송한다.
- 데이터 스토어 point-in-time 복구를 위해 이벤트 스토어를 사용할 수 있는 이벤트 소싱 패턴에 매우 중요함
- 순서가 올바르지 않으면 데이터 품질이 저하될 수 있음, 알림 순서가 유지되지 않으면 최종 일관성과 DB 롤백으로 인해 문제가 복잡할 수 있다.
- 트랜잭션 롤백: 트랜잭션이 롤백된 경우 이벤트 알림을 보내면 안 됨
- 서비스 수준 트랜잭션 처리: 여러 서비스에 걸친 트랜잭션에서 데이터 스토어 업데이트가 요구되는 경우, Saga 오케스트레이션 패턴을 사용하여 데이터 스토어 전체의 데이터 무결성을 유지한다.
Transactional Outbox Pattern
데이터베이스 트랜잭션과 메시지 브로커(Kafka)간의 원자성을 보장하기 위해 사용되는 패턴.
서비스가 데이터베이스에 변경 사항을 저장할 때, Kafka에 이벤트를 보 수 있도록 설계한다.
동작 방식
- 애플리케이션은 DB에 변경 사항을 저장하는 트랜잭션을 실행한다.
- 같은 트랜잭션 내에서 Outbox 테이블에 이벤트 데이터를 저장한다.
- 트랜잭션이 커밋되면, Outbox 테이블에는 새 이벤트가 기록된 상태가 된다.
- 별도의 Outbox Polling 프로세스 또는 CDC(Change Data Capture)를 사용하여 Outbox 테이블의 데이터를 Kafka로 전송한다.
- Outbox 테이블의 데이터는 전송이 완료된 후 삭제되거나, 별도 보관된다.
장점
- 데이터 일관성 보장: 데이터베이스 변경과 이벤트 발행이 하나의 트랜잭션으로 처리되므로, 메시지 손실 위험이 없음
- Kafka 장애 시 안전: Kafka가 다운되더라도 Outbox 테이블에 데이터가 남아있어 이후 재처리 가능
단점
- Outbox 테이블을 지속적으로 모니터링하는 추가적인 작업 필요(Polling 또는 CDC 필요).
- 테이블이 커지면 성능 저하 가능
구현 방식
- Outbox 테이블 생성
- JPA 트랜잭션 내에서 Outbox 테이블에 기록
- Scheduler 기반 Polling, Debezium을 활용한 CDC 방식, Spring Event 활용 Kafka 전송
CDC(Change Data Capture)
데이터베이스의 변경 사항을 감지하여 Kafka와 같은 이벤트 브로커로 전달하는 방식
Outbox Pattern과 함께 사용되기도 하고, 직접 데이터 변경을 감지하여 이벤트를 방행하기도 한다.
동작 방식
- 애플리케이션이 데이터베이스에 변경 사항을 저장한다.
- CDC 툴(예: Debezium)이 데이터베이스의 Change Log(binlog, WAL log 등)를 감지한다.
- 변경된 데이터를 Kafka로 실시간 스트리밍한다.
- Kafka의 Consumer가 해당 이벤트를 처리한다.
장점
- Polling 필요 없음: DB 트랜잭션이 끝난 후 자동으로 Kafka로 변경 사항이 전달됨.
- 성능 우수: DB의 로그를 직접 읽어 이벤트를 발행하므로, 별도의 조회(Query) 비용이 없음.
- 이벤트 드리븐 아키텍처에 적합.
단점
- DB 로그를 읽는 CDC 시스템(Debezium 등)이 필요함.
- 모든 변경 사항이 Kafka로 전송되므로, 불필요한 이벤트 필터링이 필요할 수도 있음
Kafka + Debezium을 활용한 CDC 방식
- PostgreSQL, MySQL 등에서 binlog/WAL 활성화
- Debezium Kafka Connector 설정
- Kafka Consumer가 변경 사항을 수신 후 처리
Transactional Outbox vs CDC
어떤 방식을 선택해야 될까?
- 데이터 일관성이 중요한 경우
- Transactional Outbox Pattern이 더 적합함.
- 데이터베이스와 Kafka 이벤트 발행을 하나의 트랜잭션으로 처리할 수 있음.
- 데이터 변경을 자동으로 감지하고 싶은 경우
- CDC 방식이 더 적합함.
- DB 변경 사항을 Kafka로 자동 발행할 수 있어 Polling 부담이 없음.
- Kafka 장애에 대비해야 하는 경우
- Transactional Outbox Pattern이 유리함.
- Outbox 테이블에 데이터가 남아 있어, Kafka가 일시적으로 장애가 발생해도 나중에 재처리 가능함.
- 성능이 중요한 경우
- CDC 방식이 Polling 없이 로그 기반으로 이벤트를 감지하므로 성능이 우수함.
결론
- Transactional Outbox Pattern: 데이터 일관성이 중요하고, Kafka 장애를 대비해야 할 때.
- CDC 방식: 데이터 변경을 자동 감지하고, Polling 없이 성능을 최적화하고 싶을 때.
- 둘을 함께 사용하면 Outbox 테이블을 CDC로 감지하여 Kafka로 이벤트를 발행하는 형태로 구현할 수도 있음.
정리
- Kafka 오류에 발생이나 데이터 일관성 유지를 위한 방식인 Transactional Outbox Pattern과 CDC(Change Data Capture)에 대해 배웠다.
- 프로젝트에 더 적합한 방식을 고려해서 한 번 적용해보는 과정이 필요할 것 같다.
'자바 심화 > TIL' 카테고리의 다른 글
Redisson 분산 락 구현 (0) | 2025.01.15 |
---|---|
Kafka 비동기 처리 구현 (0) | 2025.01.10 |
Kafka - 비동기 처리 (1) | 2024.12.31 |
동시성 제어 시점과 데이터 일관성 유지 관점 (1) | 2024.12.30 |
Redis - Redisson (2) | 2024.12.27 |