Posts Debezium MySQL Connector Snapshot 알아보기
Post
Cancel

Debezium MySQL Connector Snapshot 알아보기

Snapshot


소스 DB의 현재 데이터와 스키마 구조를 캡처해, 변경 이벤트 스트리밍의 기준선을 만드는 초기 동기화 과정

  • Debezium MySQL 커넥터가 시작될 때, 데이터베이스의 일관된 초기 스냅샷(initial consistent snapshot)을 수행한다.
  • 이 스냅샷은 커넥터가 현재 데이터베이스 상태의 기준선(baseline)을 설정할 수 있도록 해준다.
  • Debezium은 스냅샷을 실행할 때 여러 가지 모드를 사용할 수 있으며, 어떤 모드로 실행할지는 snapshot.mode 설정에 의해 결정된다.
  • 커넥터는 스냅샷을 수행할 때 여러 단계의 작업을 수행하며, 이 단계들은 스냅샷 모드와 데이터베이스에서 적용 중인 테이블 잠금 정책에 따라 달라진다.
    • 예를 들어, 글로벌 읽기 잠금(global read lock) 또는 테이블 단위 잠금(table-level locks)을 사용하는 초기 스냅샷을 수행할 때, 각기 다른 단계를 거친다.

커넥터가 시작될 때 ?

소스 DB와 연결(connection)을 맺고, Task를 실행하기 시작하는 시점

상황설명
새 커넥터를 등록할 때/connectors POST 호출 시, 커넥터 Task가 처음 시작됨
기존 커넥터를 재시작할 때/connectors/{name}/restart 또는 Kafka Connect 자체 재시작 시
커넥트 클러스터 재기동 시connect worker가 올라오면서 task가 다시 시작됨
커넥터 설정만 업데이트할 때특정 config 변경 시 Task 재시작이 포함될 경우만 해당

snapshot.mode

커넥터가 시작될 때 스냅샷을 실행할지 여부와 실행 기준을 결정 (Default : initial)

always

  • 커넥터가 시작될 때마다 항상 스냅샷을 수행한다.
  • 스냅샷에는 캡처 대상 테이블의 스키마와 데이터가 모두 포함된다.

initial

  • 커넥터가 이전에 오프셋을 기록한 적이 없거나, 이전 스냅샷이 완료되지 못한 상태로 중단되었을 때만 스냅샷을 수행한다.
  • 스냅샷이 완료된 후에는 바이너리 로그(binlog)를 읽어 변경 이벤트 스트리밍을 시작한다.

initial_only

  • 이전에 오프셋이 없을 때만 스냅샷을 수행한다.
  • 스냅샷이 끝난 후에는 커넥터가 종료되며, binlog를 통해 변경 이벤트 스트리밍을 하지 않는다.

no_data

  • 테이블 데이터는 캡처하지 않고, 스키마 정보만 캡처한다.
  • 즉, 데이터의 일관된 스냅샷은 필요 없고, 최근의 스키마 변경 사항만 반영하고 싶을 때 사용하는 모드이다.
    • 예를 들어 “데이터는 이미 target DB에 있고, 스키마만 맞춰야 하는 경우”에 유용

recovery

  • 손상되거나 유실된 스키마 히스토리 토픽을 복원할 때 사용한다.
  • 커넥터가 재시작되면, 소스 테이블의 구조를 읽어 스키마 히스토리 토픽을 다시 빌드한다.
  • 또한, 예기치 않게 커진 스키마 히스토리 토픽을 주기적으로 정리(prune) 할 수도 있다.
  • 단, 커넥터가 종료된 이후 DB에 새로운 스키마 변경이 적용된 경우, 이 모드를 사용해서는 안 됩니다. (스키마 불일치 위험)

never

  • 커넥터가 시작될 때 스냅샷을 수행하지 않고, 즉시 binlog 스트리밍을 시작한다.
  • 즉, 기존 데이터는 무시하고 이후 변경만 추적한다.
  • 이 옵션은 앞으로 no_data로 대체될 예정이다. (deprecated 예정)

when_needed

  • 커넥터가 시작된 후, 아래 조건 중 하나라도 충족되면 자동으로 스냅샷을 수행한다:
    • Kafka 토픽에서 이전 오프셋 정보를 찾을 수 없는 경우
    • 이전에 저장된 오프셋(binlog 위치 또는 GTID)이 서버에서 더 이상 유효하지 않은 경우 (예: binlog가 삭제됨, position이 유효하지 않음 등)
  • 즉, 필요할 때만 스냅샷을 다시 수행하는 “자기 복구형 모드”이다.

configuration_based

  • 이 모드를 사용하면, snapshot.mode.configuration.based.* 로 시작하는 커넥터 설정 속성을 통해 스냅샷 동작을 세부적으로 직접 제어할 수 있다.
  • 즉, 구성 파일 기반으로 세밀한 조건 지정이 가능하다.

custom

  • snapshot.mode.custom.name 속성에 정의된 사용자 구현체(io.debezium.spi.snapshot.Snapshotter 인터페이스 구현)를 이용해 스냅샷을 수행한다.
  • 즉, 사용자가 직접 구현한 스냅샷 로직을 사용한다.

글로벌 읽기 잠금(global read lock)을 사용하는 초기 스냅샷


Step1 : 데이터베이스 연결

Step2 : 캡처할 테이블 결정

  • 기본적으로 커넥터는 시스템 테이블을 제외한 모든 테이블의 데이터를 캡처한다.
    • 시스템 테이블 : MySQL 서버의 동작 및 설정, 성능 모니터링 등을 위한 내부용 테이블
      • 예 : information_schema.TABLES, performance_schema.threads, mysql.user
  • 스냅샷이 완료된 후, 커넥터는 지정된 테이블의 데이터 변경 사항을 스트리밍한다.
  • 특정 테이블만 캡처하고 싶다면, table.include.list 또는 table.exclude.list 등의 설정을 사용한다.

Step3 : 글로벌 읽기 잠금 획득

  • 캡처할 테이블에 대해 글로벌 읽기 잠금을 획득하여 다른 데이터베이스 클라이언트의 쓰기 작업을 차단한다.
  • 단, 스냅샷 자체는 다른 클라이언트가 DDL 문을 실행하는 것을 완전히 막지는 못한다.
    • 이러한 DDL은 커넥터가 binlog 위치나 테이블 스키마를 읽는 과정에 영향을 줄 수 있다.
    • 커넥터는 binlog 위치를 읽는 동안 잠금을 유지한다.

Step4 : 트랜잭션 시작

  • Repeatable Read 격리 수준으로 트랜잭션을 시작하여, 이 트랜잭션 내의 모든 읽기 작업이 일관된 스냅샷 상태를 기준으로 수행되도록 한다.
  • 만약 스냅샷 수행 시간이 너무 오래 걸린다면, 다른 격리 수준 설정을 사용하거나 초기 스냅샷을 건너뛰고 증분 스냅샷(incremental snapshot)을 수행하는 방법을 고려할 수 있다.

Step5 : 현재의 binlog 위치를 읽음

  • 이 위치는 이후 변경 데이터 스트리밍을 시작할 기준점이 된다.

Step6 : 캡처 대상 테이블들의 스키마를 캡처

  • 커넥터는 스키마 정보를 내부의 schema history 토픽에 저장하며, 이 안에는 필요한 모든 DROP, CREATE DDL 문도 포함된다.
  • schema history는 변경 이벤트가 발생했을 때 적용 중인 구조에 대한 정보를 제공한다.
  • 기본적으로 커넥터는 데이터 캡처 설정 여부와 상관없이 모든 테이블의 스키마를 캡처한다.
    • 단, 캡처 대상으로 지정되지 않은 테이블의 경우, 데이터는 캡처하지 않고, 구조만 저장한다.

Step7 : (3단계에서 획득한) 글로벌 읽기 잠금 해제

  • 다른 데이터베이스 클라이언트들의 쓰기 작업이 가능해진다.

Step8 : (5단계에서 읽은) binlog 위치를 기준으로, 지정된 테이블들을 스캔하기 시작

  • 이 과정에서 커넥터는 다음 작업들을 수행한다.
    • 스냅샷 시작 전에 생성된 테이블인지 확인
    • 스냅샷이 시작된 후 새로 생성된 테이블이라면 해당 테이블은 건너뜀
    • 다만, 스냅샷이 끝나고 커넥터가 스트리밍 모드로 전환된 후에는, 그 시점 이후 생성된 테이블에 대한 변경 이벤트를 생성
    • 테이블의 각 행에 대해 read 이벤트를 생성
    • 이 이벤트들은 모두 동일한 binlog 위치를 포함
    • 각 read 이벤트를 해당 테이블에 대응하는 Kafka 토픽으로 전송
    • 필요하다면, 사용된 데이터 테이블 잠금을 해제
  • 즉, 초기 스냅샷은 캡처 대상 테이블의 각 행의 현재 상태를 저장한다.
  • 이렇게 생성된 기준 상태(baseline)를 바탕으로, 커넥터는 이후 발생하는 모든 변경 사항을 순차적으로 캡처한다.

Step9 : 트랜잭션 커밋

Step10 : 스냅샷이 성공적으로 완료되었음을 커넥터의 오프셋(offset)에 기록

  • 이 오프셋 정보는 이후 재시작 시 커넥터가 어디서부터 데이터를 읽어야 하는지를 결정하는 기준이된다.

정리(예시 상황)

시간액션설명
10:00글로벌 읽기 잠금 획득 및 트랜잭션 시작consistent snapshot 생성
10:00binlog pos = 20000 기록baseline 위치 저장
10:01잠금 해제다른 클라이언트들이 다시 쓰기 가능
10:02DML 발생 (UPDATE, INSERT 등)binlog에 pos 20001 이후로 기록됨
10:03 ~ 10:05Debezium이 스냅샷 데이터 SELECT여전히 10:00 시점 스냅샷 뷰를 읽음
(Repeatable Read 격리 수준의 일관된 읽기)
10:05 이후Debezium이 binlog 20000부터 스트리밍 시작스냅샷 중 발생한 변경부터 순서대로 반영

테이블 단위 잠금(table-level lock)을 사용하는 초기 스냅샷


  • 일부 데이터베이스 환경에서는 글로벌 읽기 잠금을 허용하지 않는 경우가 있다.
  • 이때 Debezium MySQL 커넥터는 글로벌 락을 사용할 수 없음을 감지하면, 테이블 단위 잠금을 사용하여 스냅샷을 수행한다.
  • 이 방식을 사용하려면, Debezium 커넥터가 MySQL에 접속할 때 사용하는 데이터베이스 계정에 LOCK TABLES 권한이 반드시 있어야 한다.

글로벌 읽기 잠금때와 동일한 단계는 (위와 동일)로 표시

Step1 : 데이터베이스 연결 (위와 동일)

Step2 : 캡처할 테이블 결정 (위와 동일)

Step3 : 테이블 단위 잠금 획득

  • 캡처 대상이 되는 각 테이블에 대해 테이블 단위 잠금을 획득한다.
  • 각 테이블 단위로 읽기 전용 잠금(READ LOCK)을 설정하여 해당 테이블에 쓰기 작업이 일시적으로 차단된다.

Step4 : 트랜잭션 시작 (위와 동일)

Step5 : 현재의 binlog 위치를 읽음 (위와 동일)

Step6 : 캡처 대상 테이블들의 스키마를 캡처 (위와 동일)

Step7 : 데이터 읽기

  • (Step 5에서 얻은) binlog 위치를 기준으로, 커넥터는 캡처 대상 테이블을 순서대로 스캔하며 데이터를 읽습니다.
  • 이 과정에서 커넥터는 다음 작업을 수행합니다:
    • 스냅샷이 시작되기 전에 생성된 테이블인지 확인
    • 만약 스냅샷 도중에 새로 생성된 테이블이라면 이 스냅샷에서는 건너뛰고, 나중에 스트리밍 모드로 전환되면 그때부터 해당 테이블의 변경 이벤트를 캡처
    • 각 테이블의 모든 행을 읽어 “read event”를 생성
    • 이 이벤트에는 Step5에서 얻은 동일한 binlog 위치가 포함되어 있어, “스냅샷 전체가 같은 시점에서 찍힌 데이터임”을 보장한다.
  • 이렇게 생성된 이벤트들을 Kafka 토픽으로 전송한다.
  • 테이블 단위 잠금이 설정되어 있었다면, 테이블 데이터를 모두 읽은 후 잠금을 해제한다.

Step8 : 트랜잭션 커밋

Step9 : 잠금 해제

Step10 : 스냅샷이 성공적으로 완료되었음을 커넥터의 오프셋(offset)에 기록 (위와 동일)

정리 (예시 상황)

시각이벤트설명
10:00Debezium 스냅샷 시작binlog pos = 1000 기록
10:00테이블 A 스캔 시작(락 유지 중)
10:01사용자 UPDATE A (불가 — 잠금 중)막힘
10:01사용자 UPDATE Bbinlog pos = 1001
10:02테이블 A 스캔 완료 → UNLOCK TABLE A 
10:02테이블 B 스캔 시작(락 유지 중)
10:03테이블 B 스캔 완료 
10:05Debezium 스냅샷 완료 
10:06Debezium 스트리밍 모드 전환 (binlog pos=1000부터) 
1
2
- 글로벌 읽기 잠금 : 잠금 해제 --> 데이터 읽음
- 테이블 단위 잠금 : (테이블별로) 데이터 읽음 --> 잠금 해제

참고 자료


This post is licensed under CC BY 4.0 by the author.