kudu table copy 로 테이블을 옮기는 중 일정량을 넣다가 실패한다.
Already present: key already present
대상 테이블의 데이터를 DELETE 로 모두 지우고 다시 해도, 심지어 테이블을 지웠다 다시 만들어도 같은 지점에서 다시 난다.
kudu table copy 는 기본적으로 INSERT 로 쓴다. INSERT 는 기본키가 이미 있으면 실패한다. 즉 이 도구는 중간에 실패한 지점부터 다시 돌리는 것을 전제로 하지 않는다. 한 번 실패한 뒤 같은 명령을 다시 돌리면, 앞서 들어간 행을 다시 넣으려다 그 자리에서 또 실패한다.
"다 지웠는데도 난다" 는 관찰은 대개 지운 대상이 실제 대상 테이블이 아니었거나(마스터가 여럿이면 impala::db.table 과 db.table 이 다른 대상이다), 지운 뒤 다시 돌린 복사가 또 중간에 끊겨 같은 상황을 반복한 것이다.
Kudu 의 DELETE 는 즉시 반영된다. 지운 키를 다시 넣지 못하게 막는 유예 기간 같은 것은 없으므로, 삭제가 덜 반영돼서 나는 오류로 보지 않는다.
복사를 멱등하게 만든다. 쓰기 방식을 upsert 로 바꾸면 이미 있는 키는 갱신되므로 몇 번을 다시 돌려도 같은 결과가 된다.
kudu table copy <SRC_MASTERS> <TABLE> <DST_MASTERS> -write_type=upsert
플래그 이름과 지원 값은 버전에 따라 다르므로 먼저 확인한다.
kudu table copy --help
대상을 완전히 새로 만드는 편이 더 단순할 때도 있다. 대상 테이블을 지우고 복사에 맡기면 스키마와 파티션까지 원본을 따라간다.
kudu table list <DST_MASTERS> | grep <TABLE>
kudu table delete <DST_MASTERS> <TABLE>
kudu table copy <SRC_MASTERS> <TABLE> <DST_MASTERS>
Impala 로 만든 테이블은 Kudu 쪽 이름이 impala::<db>.<table> 이다. 목록에서 정확한 이름을 확인하고 지운다. 그리고 Kudu 에서 직접 지우면 HMS 항목이 남을 수 있으므로, Impala 로 관리하는 테이블은 Impala 에서 DROP TABLE 하는 편이 낫다.
두 클러스터가 같은 Impala 에서 보이거나 중간 저장소를 거칠 수 있다면 SQL 쪽이 통제하기 쉽다.
UPSERT INTO db.target SELECT * FROM db.source;
UPSERT 는 Kudu 테이블에서만 쓸 수 있고, 기본키가 같으면 갱신한다. 중간에 실패해도 그대로 다시 돌리면 된다.
이미 있는 행을 제외하고 넣어야 하면 NOT IN 보다 조인을 쓴다.
INSERT INTO db.target
SELECT s.* FROM db.source s
LEFT JOIN db.target t ON s.key = t.key
WHERE t.key IS NULL;
대상을 비우고 시작하려면 DELETE 보다 TRUNCATE 가 훨씬 가볍다. DELETE 는 행마다 삭제 표시를 기록하므로 큰 테이블에서는 오래 걸리고 디스크도 늘어난다.
TRUNCATE TABLE db.target;
복사 뒤에는 건수를 대조한다. 복사 도구가 성공으로 끝나도 중간에 건너뛴 행이 있을 수 있다.
SELECT count(*) FROM db.source;
SELECT count(*) FROM db.target;
kudu cluster ksck <DST_MASTERS> -tables=<TABLE>