Change Data Capture의 약자이다.
DB에 있는 데이터에 대한 변경을 감지해 필요한 후속처리(데이터 전송/공유 등)를 자동화하는 기법을 통칭한다.
직접 메시지를 발행하는 거랑 뭐가 다른가?
데이터 변경사항을 Kafka로 전달하는 방식은 크게 2가지로 컨셉을 구분할 수 있을 것 같다. 차이점은 아래와 같다.
① 데이터 변경사항을 Application 기반으로 메시지를 발행하는 컨셉
Application단에서,
- DB에도 변경사항을 반영하고
- Kafka에도 메시지를 발행해야한다.
즉, DB와 Kafka에 Dual write 하는 개념으로 각각 따로따로(병렬적으로) 데이터를 처리한다.
② 데이터 변경사항을 DB 기반으로 메시지를 발행하는 컨셉
Application단에서는 변경사항을 DB에만 적용한다. Kafka 메시지 발행은 신경쓰지 않는다.
DB에 반영이 완료되고 나면 자동으로 Kafka 메시지가 발행된다. 즉, 직렬적으로 데이터가 처리된다.
우리가 쓰는 대부분의 DB에는, 노드간 복제(Replication)를 위해 변경내역이 로그 형태로 관리되기 때문에 이게 가능한 것이다. 예를 들어, MySQL에서는 binlog, PostgreSQL에서는 WAL, MongoDB에서는 oplog가 CDC의 소스가 된다.
CDC를 적용하면 왜 좋은가?
위 ①의 컨셉과 같이, 데이터 변경사항에 대한 카프카 메시지 발행이 애플리케이션에 의존하는 방식은 몇가지 단점이 존재한다.
- DB에 CUD를 하는 애플리케이션이 API, Batch, Worker 등 여러가지가 있다면 그 모두에 Kafka produce 로직을 넣어줘야만 하므로 개발자가 로직 반영을 까먹으면 메시지 발행이 누락된다.
- 애플리케이션이
(1) DB에 C/U/D
단계 까지 완료한 뒤 갑자기 문제가 생겨서(2) Message produce
단계로 넘어가지 못한다면 메시지 발행이 누락된다.
반대로, 만일 ②처럼 CDC 형태로 직렬 형태로 데이터를 처리하면 이러한 단점들이 보완될 수 있다.
애플리케이션 레벨에서 데이터 변경사항을 직접 Kafka로 produce하는 로직을 개발자가 직접 추가할 일이 없다.
- 새로운 애플리케이션이 생겨도 알아서 Kafka 연동에 대한 대응이 되는 셈이다.
- 심지어 누군가 DB에 직접 C/U/D request를 날려도 Kafka로 produce가 된다.
CDC 적용의 단점은 없나?
애플리케이션 기반이 아니라서 단점도 있다.
-
table에 있는 row(또는 collection에 있는 document)레벨에서의 변경사항을 캡처하기 때문에,
- 여러 table(또는 collection)에 대한 변경사항을 하나의 Kafka Topic에 담을 수 없다.
- DB에 담기지 않는 데이터 변경사항은 Kafka message에 포함할 수 없다.
-
메시지를 직접 Produce하는 게 아니기 때문에,
-
변경된 데이터를 담을 메시지 스키마를 커스터마이징하는 게 제한적이다.
- 예컨대 operation type 구분을 사실상 C, U, D 3개로만 할 수 있다.
-
- CDC 구축 및 관리에 비용이 어느 정도 들 수 있다.
- 경우에 따라 DB에 부담이 될 수 있다.
따라서 CDC를 맹목적으로 적용하는 게 아니라, 득과 실을 잘 따져보고 적용해야 한다.
CDC는 보통 무엇으로 구축하나? : Kafka Connect
CDC는 주로 Kafka Connect로 구축하고, 이중에서 Source Connector에 해당하는 부분을 주로 CDC라고 부른다.
Kafka Connect는 아파치 카프카의 오픈소스 프로젝트 중 하나로, 카프카와 DB 사이의 파이프라인 구성을 쉽게(자동화) 해주는 프레임워크이다. Source Connector
와 Sink Connector
로 구성된다.
Source Connector
는 Kafka producer의 일종이고, Sink Connector
는 Kafka consumer의 일종이다. CDC의 역할은 Source Connector
가 한다.
CDC로 메시지가 발행되면 어떤 모습인가? : schema 예시
{
"before": null, // CREATE라서 null임
"after": {
"id": 11111,
"name": "테스트용 데이터",
"categoryId": 2,
"regionId": 6035,
...
},
"source": {
"db": "test_platform",
"table": "test_table",
...
},
"op": "c", // CREATE를 의미
"ts_ms": 1580390884335,
...
}
op가 “c”
일 때(= 데이터가 Create되었을 때)는 before가 null, op가 “d”
일 때(= 데이터가 Delete되었을 때)에는 after가 null이다. op가 “u”일 때(= 데이터가 Update되었을 때)에는 before와 after의 차이(diff)만큼이 변경된 거라고 해석하면 된다.