Issue
https://issues.apache.org/jira/browse/FLINK-38247
[FLINK-38247] MySqlChunkSplitter may continuously generate splits when using BIGINT UNSIGNED as primary key - ASF Jira
MySqlChunkSplitter may continuously generate splits when using BIGINT UNSIGNED as primary key, The following log illustrates this point: 2025-08-12 18:10:37,885 INFO org.apache.flink.cdc.connectors.mysql.source.assigners.MySqlChunkSplitter [] - Use unevenl
issues.apache.org
증상
(현재 flink-cdc 3.5.0 이하 버전에서 MySQL 커넥터를 사용해 BIGINT UNSIGNED 타입의 PK를 사용 시 발생하는 이슈입니다.)
MySqlChunkSplitter가 테이블을 chunk로 분할할 때 무한 루프에 빠지는 현상이 발생합니다.
발생 로그
2025-08-12 18:10:37,885 INFO MySqlChunkSplitter - Use unevenly-sized chunks for table lms_orderservice_0.order_attach_volume_charge_0, the chunk size is 8096 from 9159518964553691904
2025-08-12 18:10:37,892 INFO MySqlChunkSplitter - Use unevenly-sized chunks for table lms_orderservice_0.order_attach_volume_charge_0, the chunk size is 8096 from 9228590553717701376
2025-08-12 18:10:37,899 INFO MySqlChunkSplitter - Use unevenly-sized chunks for table lms_orderservice_0.order_attach_volume_charge_0, the chunk size is 8096 from 68365677240266752
2025-08-12 18:10:37,907 INFO MySqlChunkSplitter - Use unevenly-sized chunks for table lms_orderservice_0.order_attach_volume_charge_0, the chunk size is 8096 from 136590545025291264
2025-08-12 18:10:38,015 INFO MySqlChunkSplitter - ChunkSplitter has split 39800 chunks for table lms_orderservice_0.order_attach_volume_charge_0
이상한 점
로그를 자세히 보면 chunk의 시작 값이 비정상적으로 변합니다
| 순서 | Chunk 시작 값 | 분석 |
| 1 | 9,159,518,964,553,691,904 | Long.MAX_VALUE 근처 |
| 2 | 9,228,590,553,717,701,376 | Long.MAX_VALUE 초과 |
| 3 | 68,365,677,240,266,752 | 갑자기 작은 값으로 떡락! |
| 4 | 136,590,545,025,291,264 | 다시 여기서부터 증가 |
-> 정상적이라면 값이 계속 증가하다가 테이블의 최댓값에 도달하면 종료되어야 하는데, 이런 식의 무한 루프에 빠지고 있었습니다.
원인
1. 데이터 타입 범위 차이
MySQL의 BIGINT UNSIGNED 타입과 Java의 Long 타입은 서로 다른 범위를 가지고 있습니다.
- MySQL BIGINT UNSIGNED
- 범위: 0 ~ 18,446,744,073,709,551,615 (2^64 - 1)
- Java long
- 범위: -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 (2^63 - 1)
-> Java의 long은 MySQL BIGINT UNSIGNED 최대값의 절반밖에 표현하지 못합니다.
이 타입 범위가 문제인 이유는 MySQL 커넥터의 동작 방식이 문제가 있기 때문입니다.
2. MySQL Connector/J의 setObject() 동작
if (parameterObj instanceof BigInteger) {
setLong(parameterIndex, ((BigInteger) parameterObj).longValue());
}
flink-cdc는 Chunk 과정에서 MySQL에 "여기부터 여기까지 가져갈거야~" 라는 값을 전달하게 되는데, 이때 BigInteger 값을 전달하면 MySQL 커넥터는 내부적으로 long으로 변환해 버립니다.
따라서 Long.MAX_VALUE를 초과하는 값은 오버플로우가 발생하게 되어 음수가 되어버릴 수 있는 위험이 있습니다.
3. 무한 Chunk Splitting 발생 메커니즘
flink-cdc의 Chunk Splitting 과정은 간략하게 설명하자면 다음과 같습니다.
- 테이블 PK의 MIN, MAX 값을 조회합니다. 여기서 PK는 CDC 파이프라인 실행 시, 사용자가 ChunkKeyColumn을 명시한 값을 의미합니다.
- 테이블의 대략적인 ROW 분포를 확인 후 Chunk 분할 방식을 결정합니다.
- Evenly-sized chunks: 균등하게 분포되어 있어 사용자가 명시한 SplitSize 만큼씩 가져오게 됩니다.
- Unevenly-sized chunks: 균등하지 않은 skew 상태로, 동적으로 chunk 경계를 찾게 됩니다.
이번 상황은 Unevenly-sized chunks으로, 동적으로 계속해서 chunk 경계를 찾는 과정을 반복합니다.
위에 적은 발생 로그가 그 과정의 일부라고 보면 됩니다.
그런데 이때, 다음 Chunk 값을 가져오는 과정에서 오버플로우가 발생해 음수가 반환되고 -> 다시 큰 값으로 증가 -> 다시 음수.. 과정을 반복하다 보니 무한 루프에 걸리게 된 것입니다.
해결
(현재 PR 진행 중으로 공식적인 해결 방법은 아닙니다.)
public static void setSafeObject(PreparedStatement ps, int parameterIndex, Object value)
throws SQLException {
if (value instanceof BigInteger) {
ps.setBigDecimal(parameterIndex, new BigDecimal((BigInteger) value));
} else {
ps.setObject(parameterIndex, value);
}
}
MySQL 커넥터에 chunk 계산에 필요한 값을 넘겨주는 부분에 위와 같은 함수를 거치도록 변경했습니다.
BigInteger 타입을 BigDecimal로 세팅하였고, MySQL 커넥터에서도 이 값은 그대로 BigDecimal로 해석하기 때문에 오버플로우의 염려는 없습니다.
또한 flink-cdc 공식 문서에도 BIGINT UNSIGNED 타입의 경우 Decimal(20,0)으로 변환하기 때문에, 이러한 처리는 문제없을 것으로 예상됩니다. (레코드 값에는 영향 X, 단지 chunkKey 계산 시에만 전달하는 값)
Github PR
https://github.com/apache/flink-cdc/pull/4117
[FLINK-38247] Handle BIGINT UNSIGNED overflow in PreparedStatement by suhwan-cheon · Pull Request #4117 · apache/flink-cdc
issue: https://issues.apache.org/jira/browse/FLINK-38247 Issue An infinite loop occurred when using the MySqlChunkSplitter to split a table with a MySQL BIGINT UNSIGNED primary key. (This problem h...
github.com
참고
- https://nightlies.apache.org/flink/flink-cdc-docs-release-3.5/docs/connectors/flink-sources/mysql-cdc/#data-type-mapping
- https://github.com/mysql/mysql-connector-j/blob/8.0.28/src/main/core-impl/java/com/mysql/cj/AbstractQueryBindings.java#L920