Apache Flink 실시간 JOIN에서 업데이트 처리: 상태 누수 문제 해결
Apache Flink 실시간 JOIN 시스템에서 소스 테이블 레코드의 업데이트는 상태 누수와 잘못된 결과를 초래할 수 있습니다. domains 테이블의 user_id 필드가 변경되면, 업데이트 이벤트가 이전 데이터를 지우지 않고 다른 TaskManager에 할당될 수 있습니다. 마찬가지로 기본 키 변경 시 이전 레코드가 제거되지 않아 하류에서 중복이 발생합니다.
상황을 세부적으로 살펴보겠습니다:
domains.id는 고정하고user_id만 변경: 새 이벤트가 다른 키로 해시되어 이전 상태가 남습니다.domains.id와user_id모두 변경: 새 레코드가 생성되지만 이전 레코드가 제거되지 않아 중복 제거가 깨집니다.
Flink의 분산 아키텍처와 로컬 존
Flink는 keyBy를 통해 데이터를 TaskManager에 분산합니다. 키의 해시 값이 소유 컨테이너를 결정하며, 이를 통해:
- 동일 키의 모든 이벤트가 한 곳에 모입니다.
- 이벤트 순서가 보장됩니다.
해시 공간은 균등하게 분배되며, 각 TaskManager가 자신의 존을 담당합니다. keyBy 없이 라운드 로빈으로 처리하면 JOIN에 부적합합니다—지역성 보장이 사라지기 때문입니다.
만능 해결책: 삭제 + 삽입
업데이트를 처리하기 위해 Debezium CDC 이벤트를 삭제(before)와 삽입(after)로 분리합니다. 순서가 중요합니다: 먼저 이전 키로 삭제, 그다음 새 키로 삽입. keyBy가 순차 처리를 보장합니다.
Domain 클래스 구현:
public class Domain implements Serializable {
public Integer id;
public Integer user_id;
public String domain_name;
public boolean delete;
// getters and setters omitted
public static Domain[] fromRow(Row row) {
Character op = (Character) row.getField(1);
if (op == null) {
throw new IllegalStateException("Never should happen, if Debezium feels fine");
}
if (op == 'd') {
Row before = (Row) row.getField(0);
return new Domain[]{
new Domain(before.getField(0), before.getField(1), before.getField(2), true) };
} else if (op == 'u') {
Row before = (Row) row.getField(0);
Row after = (Row) row.getField(2);
return new Domain[]{
new Domain(before.getField(0), before.getField(1), before.getField(2), true),
new Domain(after.getField(0), after.getField(1), after.getField(2), false)
};
} else {
Row after = (Row) row.getField(2);
return new Domain[]{
new Domain(after.getField(0), after.getField(1), after.getField(2), false)
};
}
}
}
로직:
- 삭제 ('d'): before에서 삭제 이벤트 생성.
- 업데이트 ('u'): before에서 삭제 + after에서 삽입.
- 삽입 ('c'): after에서 삽입만.
삭제는 상태에서 레코드를 제거하거나 표시하고, 삽입은 업데이트합니다. 가변 JOIN 또는 PK 키에 대해 이벤트가 상태를 스스로 정리합니다.
이 접근법의 장점
- 실시간 스트림에서 완전한 CRUD 지원.
- 누수 없고 중복 없는 상태.
- 가변 JOIN 및 PK 키 적절 처리.
keyBy로 순서 보장.
핵심 요약
- 업데이트를 삭제+삽입으로 분리하면 키 변경 시 상태 누수 해결.
- 삽입 전에 삭제가 일관성을 위한 핵심.
- Debezium의 before/after로 이전 값 접근.
- Flink 분산 환경에서 원활히 확장.
— Editorial Team
아직 댓글이 없습니다.