
| 데이터베이스의 변경 사항을 실시간으로 감지하고 스트리밍해주는 오픈소스 CDC 플랫폼
기존에는 데이터베이스의 데이터가 변경사항이 있는지 확인하기 위해 주기적으로 쿼리를 날리는 Polling 방식을 썼으나, Debezium 은 데이터베이스의 트랜잭션 로그를 직접 읽는 로그 기반 CDC 방식을 사용함
성공적으로 commit 이 발생한 데이터에 대해서만 변경사항이 전파되기 때문에 실패한 트랜잭션은 고려할 필요가 없음
Debezium 은 변경사항을 디스크에 저장하기때문에 데이터의 변경사항을 전달받아야하는 애플리케이션이 다운되더라도 문제가 없음
M: 모든 데이터에 대해서 전체 적재(전체 데이터 Overwrite) 를 통해 변경사항이 전부 반영된 최신의 데이터를 가져옴
HI: INSERT 만 되는 테이블의 성격에 해당하며, 날짜를 기준으로 증분 데이터만 가져옴
HIU: INSERT 혹은 UPDATE 되는 테이블 성격에 해당하며 날짜를 기준으로 최신 데이터(증분 데이터) 가져옴
이 3가지 모두 쿼리로 적용가능하지만 삭제된 데이터를 반영할 수 있는 것은 M 밖에 없음
하지만 데이터가 수억 건 이상으로 넘어가게 된다면, index scan 도 의미없는 full scan 특성상 운영DB 에 엄청난 부하가 가게 될 것이고, 시간과 비용 또한 무시 못하게 됨
결국 삭제를 감지하여 반영할 수 있는 방법이 필요한데 이 때 CDC 를 구현하게 됨
낮은 오버헤드 : 주기적으로 SELECT 쿼리를 날리지않으므로 데이터베이스 CPU 에 부담을 주지않고 실시간에 가까운 처리가 가능함
데이터 유실 없음: 데이터베이스에 기록되는 모든 로우 레벨 변경 사항을 추적하므로, 사소한 변경이나 삭제 이벤트까지 완벽하게 잡아냄
애플리케이션 변경 최소화: 변경 감지를 위해 테이블에 변경 시각 타임스탬프 컬럼이나 트리거를 강제로 추가할 필요가 없음
| 분류 | ① Kafka Connect 방식 (표준) | ② Debezium Server 방식 (독립형) | ③ Debezium Embedded 방식 (내장형) |
|---|---|---|---|
| 구조 | Kafka 분산 클러스터 위에서 동작 | 별도의 단독 프로세스(App)로 구동 | 자바 애플리케이션의 라이브러리로 포함 |
| Kafka 필수 여부 | 필수 | 없어도 됨 | 없어도 됨 |
| 이벤트 목적지 (Sink) | Kafka Topic | AWS Kinesis, RabbitMQ, Pub/Sub, Webhook, Redis 등 | 자바 코드 내부 인메모리 버퍼 (Consumer) |
| 장점 | 고가용성(HA), 대규모 스케일아웃, 검증된 안정성 | 카프카 없이 가볍게 다양한 클라우드 큐로 전송 가능 | 인프라 추가 부담 제로, 가장 가볍고 제어가 자유로움 |
| 단점 | 카프카 클러스터 관리 비용 및 높은 인프라 복잡도 | 클러스터링(분산 처리) 미지원, 이중화 구현이 까다로움 | 애플리케이션 종료 시 이벤트 유실 위험, 로직 직접 구현 |
- Snapshot: Debezium이 source database와 처음으로 연결됐거나 또는 모든 로그가 존재하지 않을 경우 활용하는 방식
- Filters: schema, table, column 별로 필터링 여부를 결정
- Masking: 특정 컬럼의 데이터를 masking 할 수 있음, 민감한 데이터(예를 들어 개인정보)를 취급할 때 유용한 기능
- Monitoring: JMX를 통해 connector을 모니터링
- Message transformation: 메시지를 수정할 수 있는 기능
Debezium 이 처음 시작될 때, 대상 테이블의 초기 상태(정적 데이터)를 한번 가져오는 작업
initial (기본값) : 처음 시작 시 전체 테이블을 스냅샷, 이후 binlog 로 계속 tailing
schema_only : 테이블 구조만 가져오고 데이터는 snapshot 안함 (단 binlog 는 계속 읽음)
initial_only : 최초 snapshot 만 수행, binlog 는 tailing 안함 (일회성 추출 목적)
never : snapshot,binlog 안 읽음 (기존 offset 기준부터만 tailing)
when_needed: Debezium 이 판단해서 snapshot 수행 여부 결정 (특정 커넥터 지원)
| 항목 | 영향 | 설명 |
|---|---|---|
| Snapshot 테이블 크기 | CPU, Disk I/O | 큰 테이블 SELECT는 I/O와 CPU 모두 부담 |
| binlog 크기 (offset 이후) | CPU, Memory | 오래된 binlog를 순차 처리하므로 CPU 부하 증가 가능 |
| CDC consumer 처리 속도 | Kafka backlog | Debezium이 빨리 밀어도 Kafka나 downstream이 느리면 부하 쌓임 |
| Debezium 인스턴스 리소스 | Memory, GC | JVM 메모리 부족 시 GC 폭증 및 OOM 발생 가능 |
Debezium 재시작 시 흐름
- 커넥터 시작
- kafka offset 저장소 확인
- 이전 snapshot 이 완료되었고, binlog 위치도 있다면 snapshot 건너뜀
- 저장된 binlog 위치부터 log-based CDC 수행
- offset.storage.topic 데이터가 사라졌거나 초기화됨: Debezium 은 이전 상태를 알 수 없게 되므로 snapshot 을 다시 수행
- Kafka Connect의 connector name 이 바뀜: offset이 새로운 이름(key)으로 저장되므로, 새로운 커넥터로 인식되어 snapshot 을 다시 수행
- snapshot.mode=always 로 명시: 매번 시작할 때마다 snapshot 을 강제로 수행하도록 설정된 상태
- 커넥터 구성 변경 (database.hostname, database.server.name 등): 내부적으로 새로운 connector 로 인식되어 snapshot 이 재시작될 수 있음
- pod 가 ephemeral 하고, offset 을 Kafka 가 아닌 로컬에 저장했을 경우: 재시작 시 로컬에 있던 offset 정보가 유실되므로 snapshot 이 재시작될 가능성이 매우 높음
| 항목 | 권장 설정 / 전략 |
|---|---|
| Offset 저장 방식 | offset.storage.topic을 로컬이 아닌 Kafka 내부 토픽으로 설정 |
| Connector name | 동일한 이름 유지 (connector 이름 변경 시 새로운 snapshot 유발) |
| Snapshot Mode | 의도치 않은 재시작 시 snapshot을 방지하려면 snapshot.mode를 schema_only 또는 never로 설정 고려 |
| Offset 백업 | 필요 시 주기적으로 offset 토픽 백업 (Kafka MirrorMaker 등 활용) |
| Connector 배포 시 주의 | Helm/Manifest로 connector 배포 시 매번 생성-삭제(Lifecycle)되지 않도록 주의 |
DB 에서 캡처한 메시지를 토픽에 저장하기 직전에 변형,가공 하는 기능
{
"name": "mysql-debeizum-connector",
"config": {
"connector.class": io.debezium.connector.mysql.MySqlConnector",
"task.max": "1",
// --- SMT ExtractNewRecordState 설정 ---
// 별칭을 설정함
"transforms": "unwrap",
//별칭이 어떤 Java 클래스를 사용할지 지정
"transforms.unwrap.type":"io.debezium.transforms.ExtractNewRecordState",
// 만약 op : DELETE 일 때 토픽에 null 메시지를 보낼지 여부
"transforms.unwrap.drop.tombstones": "true",
// 테이블 삭제 시 동작 여부
"transforms.unwrap.delete.handling.mode": "drop"
}
}
{
"name": "postgres-debezium-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
// --- SMT Filter 설정 ---
"transforms": "filter",
// 필터 변환 클래스 지정
"transforms.filter.type": "io.debezium.embedded.Transform$Filter",
// 표현식 언어로 JSR-223(JavaScript) 사용
"transforms.filter.language": "jsr223.groovy",
// 필터링 조건 메서드 (예: 'status' 컬럼 값이 'COMPLETED'인 데이터만 통과시킴)
// 아래 조건이 'true'를 반환하는 이벤트만 카프카에 적재
"transforms.filter.condition": "value.after?.status == 'COMPLETED'"
}
}
Reference
https://debezium.io
https://spidyweb.tistory.com/604