python-replication-mysql는 Master에 최대한 부하를 주지 않고 데이터 분석, CDC를 가능하게 만드는 프로젝트이다.
국내기업에서는 카카오, 원티드 해외기업에서는 아마존에서 CDC로 사용되고있는 오픈소스 프로젝트이다.
개인적으로 아래 재밌게 본 영상인데 해당 DB장애도 BINLOG 기반으로 복구하였다.
python-replication-mysql도 Binlog 바탕으로 DDL, DML을 python 객체로 다시한번 wrapping 해주는 프로젝트로 보면된다.
그렇다면 같은 상황에서 python-replication-mysql로 DB를 복구해 나갈수 있었을까?
정답은 그때는 안된다. 하지만 오픈소스 기여로 이젠 할수있다.
1. 문제 원인
위와 같은 비슷한 예제이다
1) 내가 매일 9시에 새로운 테이블을 생성하고 이전 테이블을 Drop 시키는 일을 한다.
2) 10시에 이전 Drop된 테이블에 대한 정보를 Binlog기반으로 백업용 DB에 다시 넣는다.
2번을 작업할때 python-replication-mysql 오픈소스를 사용하면 문제가 발생한다.
원인은 현재 데이터베이스 테이블의 구조에 의지하기 때문이다.
자세히 설명하자면 TableMapEvent가 발생시 Binlog의 패킷을 통해 schema,table 이름의 값을 얻어올 수 있는데 이를 바탕으로 실제 Master DB에 아래와 같이 쿼리를 날린다.
SELECT
COLUMN_NAME, COLLATION_NAME, CHARACTER_SET_NAME,
COLUMN_COMMENT, COLUMN_TYPE, COLUMN_KEY, ORDINAL_POSITION,
DATA_TYPE, CHARACTER_OCTET_LENGTH
FROM
information_schema.columns
WHERE
table_schema =":schema_name" and table_name = ":table_name"
ORDER BY ORDINAL_POSITION
따라서 Drop된 테이블에 대해서 현재 데이터베이스의 테이블이 존재하지 않기 때문에 DELETE,UPDATE,INSERT시 칼럼 맵핑을 하지못하는 치명적인 문제가 발생한다.
2. 해결방법
이벤트 시점의 테이블 칼럼정보를 흭득해야 한다.
- CDC 사용
- Debezium으로 Kafka connect를 사용하여 DDL 변경건에 대해서 Consume한다.
- kafka에 의존적인 관계 생성
- kafka consume이 매번 python-replication-mysql 애플리케이션 이벤트 받는시점보다 앞서서 받는것을 보장해야한다.
- Table Map Event의 optional metadata 사용 ✅
- Mysql 8.0부터 optional metadata를 제공
- optional metadata는 이벤트 발생시점의 칼럼 정보를 Binlog에 저장해서 전달해줌
- binlog_row_metadata ='FULL' 변수 설정해야함
1번은 치명적인 문제가있었다.
kafka consumer는 Binlog의 이벤트가 들어오는 순서보다 무조건 먼저 들어와야된다는 것을 보장해야한다.
DDL 변경된 것이 더 늦는다면 Debezium을 붙이는 의미가 없기 때문에 2번으로 결정했다.
2번의 한계도 분명히 존재했는데 MYSQL 8.0 이전 사용자들은 binlog_row_metadata를 설정할 수 없다.
3. 개발 계획
개발을 진행하기 앞서 더이상 BINLOG의 패킷을 통해 들어온 정보들을 제외하고 실제 데이터베이스에 의존적이면 안된다고 생각했다.
위 SELECT에서 받아온 정보 DATA_TYPE, COLUMN_COMMENT, CHARACTER_OCTET_LENGTH이 당장의 이득이 될수 있어도 먼 미래를 생각한다면 다 제거하는게 옳다고 생각했다.
메인테이너에게 이 주제에 대해 얘기를 나누고 MYSQL 5.7 사용자들에게는 기존 0.44 버전까지만을 제공하자고 제안했다.
https://github.com/julien-duponchelle/python-mysql-replication/issues/473
제안에 대한 답변
따라서 총 3가지 작업을 진행했다.
1. 현재 데이터베이스의 Column Schema 의존성 제거
3. 추출한 값을 토대로 이벤트 시점의 column 업데이트
4. 보충 설명
optional_meta_data는 event 발생시점의 Columns의 정보가 Binlog에 저장되어있다.
실제 Binlog 패킷을 파씽해서 데이터를 얻을 수 있다.
파씽 결과
=== OptionalMetaData ===
unsigned_column_list: [False, False]
default_charset_collation: None
charset_collation: {}
column_charset: []
column_name_list: ['id', 'json_data']
set_str_value_list : []
set_enum_str_value_list : []
geometry_type_list : []
simple_primary_key_list: [0]
primary_keys_with_prefix: {}
visibility_list: [True, True]
charset_collation_list: []
enum_and_set_collation_list: []
5. 마치며
위 이슈에 대해 같은 고민을 했던 오픈소스 사용자들의 반응도 흥미로웠다.
python-replication-mysql 이 0버전대가 아닌 1버전대로 올라가는 계기도 되었다.
Reference