postgresql 에서 논리적 복제를 통해 CDC 하는 과정에서
여러가지 plugin이 존재한다
그 중 pgoutput으로 변화된 데이터를 읽고 파싱하기 위해 데이터의 포멧을 알아보았다
기존에 test_decoding plugin을 사용할때는 사람이 읽기 쉬운 텍스트 형태로 값이 넘어와
파싱하는데 어려움이 크게 없었지만 pgoutput을 사용하면 바이트 형태로 넘어오기 때문에
이를 몇 바이트씩 잘라야 하는지 알아내야했다
공식문서에서 제공하는 포멧을 참고해 코드를 만들었고 정상적으로 값을 뽑을 수 있는것을 확인했다
해당 포스팅은 포멧에 대한 정보만 작성할 예정이고 파싱한 코드와 결과는 다른 포스팅에서 다루겠다
1. pgoutput이란?
- postgresql 에서 논리적 복제를 위해 plugin을 설정하게 되는데 이때 기본이 되는 값
- 성능을 위해 바이트 형태로 꼭 필요한 정보로 축약해서 나타냄
- 테스트를 용이하게 하기 위해 test_decoding plugin을 사용하면 사람이 보기 편하게 텍스트 형식으로 나옴
2. pgoutput 을 사용하는 이유
- 기존 test_decoding과 가장 큰 차이점은 전체 테이블을 보느냐 특정 테이블만을 바라보느냐 이다
- test_decoding의 경우 전체 테이블의 정보를 읽고 pgoutput의 경우 설정을 통해 특정 테이블만을 볼 수 있다
- 해당 포스팅은 pgoutput의 포멧을 알아보는것에 집중하여 특정 테이블 설정 방법은 추후 다른 포스팅에서 다루겠다
3. pgoutput 포멧
- 순서는 Begin -> Insert, Update, Delete -> Commit 순이고 Insert 앞에는 Relation 이라는게 나와서 스키마, 테이블명, 컬럼명 등을 알려준다
Begin
[ B (byte1) ] [ FinalLSN(int64) ] [ CommitTime(int64) ] [ TransactionId(int32) ]
- B (byte1) : 버퍼의 시작을 ‘B’ 라는 단어로 식별
- FinalLSN (Int64) : 트랜잭션 최종 LSN 정보
- CommitTime (Int64) : 트랜잭션 타임스탬프 값, 값은 Postgresql epoch (2000-01-01) 이후의 마이크로 초 단위
- TransactionId (Int32) : 트랜잭션 식별값(xid)
Relation
[ R(byte1) ] [ RelationOid(int32) ] [ SchemaName(String) ] [ TableName(String) ] [ ReplicaIdentity(int8) ]
[ NumberOfColumns(int16) ] [ ColumnName(String) ] [ ColumnTypeOid(int32) ] [ Atttypmod(int32) ]
> ColumnName(String) 부터 Atttypmod(int32) 까지는 컬럼의 수 만큼 반복된다
- R (byte1) : 버퍼의 시작을 ‘R’ 이라는 단어로 식별
- RelationOid (Int32) : 테이블 객체 식별값 (Oid)
- SchemaName (String) : 스키마 값
- TableName (String) : 테이블 이름 값
- ReplicaIdentity (Int8) : 테이블에 대한 복제본 Id 설정(pg_class의 relreplident와 동일)
- NumberOfColumns (Int16) : 컬럼 총 개수
- ColumnName (String) : 컬럼 이름
- ColumnTypeOid (Int32) : 컬럼 타입 식별값
- Atttypmod (Int32) : 컬럼 수정자
Insert
[ I(byte1) ] [ RelationOid(int32) ] [ IsNew(byte1) ] [ TupleData ]
> Insert, Update, Delete에 공통으로 들어가는 TupleData 는 맨 마지막에 정리해놨다
- I (byte1) : 버퍼의 시작을 ‘I’ 라는 단어로 식별
- RelationOid (Int32) : 테이블 객체 식별값 (Oid)
- IsNew 'N‘ (byte1) : 다음차례에 나올 TupleData 메시지를 새 튜플로 식별
- TupleData : 새로운 튜플의 내용을 나타내는 부분 (Tuple 제목 참고)
Update
[ U(byte1) ] [ RelationOid(int32) ] [ keyOrTuple(byte1) ] [ TupleData ]
> Insert, Update, Delete에 공통으로 들어가는 TupleData 는 맨 마지막에 정리해놨다
- U (byte1) : 버퍼의 시작을 ‘U’ 라는 단어로 식별
- RelationOid (Int32) : 테이블 객체 식별값 (Oid)
- keyOrTuple ‘K' (byte1) : 업데이트로 인해 열의 데이터가 변경된 경우에 존재
REPLICA IDENTITY 인덱스의 일부
- TupleData : 새로운 튜플의 내용을 나타내는 부분 (Tuple 제목 참고)
Delete
[ D(byte1) ] [ RelationOid(int32) ] [ keyOrTuple(byte1) ] [ TupleData ]
> Insert, Update, Delete에 공통으로 들어가는 TupleData 는 맨 마지막에 정리해놨다
- D (byte1) : 버퍼의 시작을 ‘D’ 라는 단어로 식별
- RelationOid (Int32) : 테이블 객체 식별값 (Oid)
- keyOrTuple 'K‘ or 'O' (byte1) :
- byte1('K') : 업데이트로 인해 열의 데이터가 변경된 경우에 존재
REPLICA IDENTITY 인덱스의 일부,
- byte1('O') : 메시지를 이전 튜플로 식별함
업데이트가 발생한 테이블의 REPLICA IDENTITY 가 FULL로 설정된 경우만 존재
- TupleData : 새로운 튜플의 내용을 나타내는 부분 (Tuple 제목 참고)
Commit
[ C(byte1) ] [ CommitLSN(int64) ] [ EndLSN(int64) ] [ CommitTime(int64) ]
- C (byte1) : 버퍼의 시작을 ‘C’ 라는 단어로 식별
- CommitLSN (Int64) : 커밋 LSN 값
- EndLSN (Int64) : 트랜잭션 끝 LSN 값
- CommitTime (Int64) : 트랜잭션의 타임스탬프 커밋, 값은 postgresql epoch 이후의 마이크로초 단위
Tuple
[ NumberOfColumns(int16) ] [ n or u or t(byte1) ] [ StrLen(int32) ] [ Value(String) ]
> n or u or t(byte1) 부터 Value(String) 까지는 컬럼의 수 만큼 반복된다
- NumberOfColumns (Int16) : 컬럼 총 개수
- n or u or t (byte1) : 튜플 종류 식별
- StrLen (Int32) : 튜플 값 총 길이
- Value (String) : 튜플 값
4. 참고문헌
- https://www.postgresql.org/docs/current/protocol-logicalrep-message-formats.html (postgresql 16버전 공식문서)
- https://github.com/davecramer/LogicalDecode/blob/master/src/main/java/com/postgresintl/logicaldecoding/PgOutput.java (pgoutput 소스코드)
'DataBase' 카테고리의 다른 글
| [DataBase] postgresql 논리적 복제 plugin(pgoutput) 설정 및 파싱 (0) | 2024.05.17 |
|---|---|
| [DataBase] WITH절 알아보기 (0) | 2023.09.26 |
| [DataBase] 식별 관계와 비식별 관계 (0) | 2023.09.18 |
| [DataBase] TimescaleDB 특징, 설치 방법 및 테스트 (0) | 2023.08.04 |
| [DataBase] JPA @Transactional 없이 수동으로 처리 해보기 (0) | 2023.07.26 |