업서트의 세그먼트 컴팩션
업서트의 세그먼트 컴팩션 (Segment Compaction on Upserts)
업서트가 활성화된 실시간 테이블에서 세그먼트 컴팩션을 사용하는 페이지예요.
본문
세그먼트 컴팩션 개요
세그먼트를 컴팩션하면 완료된 세그먼트를 레코드의 최신 버전만 포함하는 컴팩션된 세그먼트로 교체해요. Pinot 실시간 테이블에서 업서트를 사용하는 방법에 대한 자세한 내용은 스트림 수집 + Upsert 참고.
Pinot 업서트 기능은 불변 세그먼트에 수집된 레코드의 모든 버전을 저장해요. 이전 버전은 쿼리되지 않지만 계속 스토리지 오버헤드를 추가해요. 쿼리 결과에 더 이상 사용되지 않는 이전 레코드를 제거하고 스토리지 공간을 회수하려면 Pinot 세그먼트를 주기적으로 컴팩션해야 해요. 세그먼트 컴팩션은 새 minion 태스크로 수행돼요. Pinot 태스크를 주기적으로 스케줄링하려면 Minion 문서 참고.
실시간 테이블의 업서트 세그먼트 컴팩션
업서트 세그먼트를 컴팩션하려면 다음 단계를 완료해요:
- 태스크 스케줄링이 활성화되어 있고 minion이 사용 가능한지 확인.
- 테이블 설정에 다음을 추가. 이 설정들(
schedule제외)이 어떤 세그먼트를 컴팩션할지 결정해요.
"task": {
"taskTypeConfigsMap": {
"UpsertCompactionTask": {
"schedule": "0 */5 * ? * *",
"bufferTimePeriod": "7d",
"invalidRecordsThresholdPercent": "30",
"invalidRecordsThresholdCount": "100000",
"tableMaxNumTasks": "100",
"validDocIdsType": "SNAPSHOT"
}
}
}
bufferTimePeriod:세그먼트가 완료되면 즉시 컴팩션하려면"0d"로 설정. 컴팩션을 지연하려면(위 설정처럼 7일("7d") 지연) 세그먼트 완료 후 컴팩션을 지연할 일 수를 지정.invalidRecordsThresholdPercent(선택) 완료된 세그먼트에서 허용되는 이전 레코드를 세그먼트 전체 레코드 수의 백분율로 제한. 위 예시에서는 세그먼트 레코드의 30%가 오래되면 완료된 세그먼트가 컴팩션 대상으로 선택될 수 있음.invalidRecordsThresholdCount(선택) 완료된 세그먼트에서 허용되는 이전 레코드를 레코드 수로 제한. 위 예시에서 세그먼트가 100K보다 많은 레코드를 포함하면 컴팩션 대상으로 선택될 수 있음.tableMaxNumTasks(선택) 스케줄링할 수 있는 태스크 수를 제한.validDocIdsType(선택) 데이터 컴팩션 실행 시 가져올 validDocIds의 출처를 지정. 유효 값은SNAPSHOT,SNAPSHOT_WITH_DELETE,IN_MEMORY,IN_MEMORY_WITH_DELETE예요.upsertConfig.deleteRecordColumn이 구성되어 있어도SNAPSHOT이 기본값으로 유지되며, Pinot은 설정된 값을 있는 그대로 존중해요.SNAPSHOT: 기본 validDocIds 유형. Pinot 세그먼트에서validDocIds스냅샷을 로드. 이 유형에는upsertConfig.snapshot이DISABLE이 아니어야 함.SNAPSHOT_WITH_DELETE: Pinot 세그먼트에서 delete-awarequeryableDocIds스냅샷을 로드.upsertConfig.snapshot이DISABLE이 아니어야 하고upsertConfig.deleteRecordColumn이 설정되어야 함.IN_MEMORY: 실시간 서버의 메모리에서validDocIds비트맵을 로드.IN_MEMORY_WITH_DELETE: 실시간 서버의 메모리에서 delete-awarequeryableDocIds비트맵을 로드. 이 유형에는upsertConfig.deleteRecordColumn이 설정되어야 함.
{% hint style="warning" %} 두 인메모리 유형을 사용할 때, 서버가 재시작되면 서버가 시작 전에 수집했던 데이터를 다시 수집한 후에 업서트 뷰가 다시 일관성 있게 됩니다. 인메모리 비트맵은 서버가 소비 중인 세그먼트(커밋 전)에 데이터를 수집할 때 업데이트되기 때문에, 서버가 데이터를 소비하는 동안 재시작되면 이전에 수집한 데이터를 따라잡은 후에 업서트 뷰가 다시 일관성 있게 됩니다. 반면 비트맵 스냅샷은 세그먼트를 커밋한 후에만 찍히므로 서버 재시작 시 더 일관성 있을 수 있지만, 서버가 데이터 수집 중 재시작되면 결국 일관성을 얻게 됩니다. {% endhint %}
{% hint style="info" %}
세그먼트 컴팩션은 비용이 큰 연산이므로, invalidRecordsThresholdPercent과 invalidRecordsThresholdCount를 너무 낮게(1 근처) 설정하는 것을 권장하지 않습니다. 기본적으로 위 설정은 모두 0이며, 즉 임계값이 적용되지 않습니다.
{% endhint %}
예시
다음 예시는 24M 레코드와 각각 100번 중복된 240K 고유 키를 가진 데이터셋을 포함해요. 데이터 수집 후 총 추정 크기 22.8MB의 6개 세그먼트(5개 완료 + 1개 소비 중)가 있어요.
예시 데이터셋
컴팩션 전에 쿼리 "set skipUpsert=true; select count(*) from transcript_upsert"를 제출하면 24,000,000개의 결과가 나와요:
세그먼트 컴팩션 전 결과
컴팩션 태스크가 완료된 후, Minion Task Manager UI가 다음을 보고해요.
Minion 컴팩션 태스크 완료
세그먼트 컴팩션은 컴팩션할 각 세그먼트에 대해 태스크를 하나 생성해요. 이 경우 5개의 태스크가 생성됐는데, 완료된 세그먼트의 레코드 중 90%(3.6~4.5M 레코드)가 설정된 임계값을 초과해 컴팩션 준비가 된 것으로 간주되기 때문이에요.
{% hint style="info" %} 완료된 세그먼트가 이전 레코드만 포함하는 경우 Pinot은 (컴팩션 태스크를 만드는 대신) 세그먼트를 즉시 삭제합니다. {% endhint %}
쿼리를 다시 제출하면 count가 240K 고유 키 집합과 일치함을 보여 줘요.
세그먼트 컴팩션 후 결과
세그먼트 컴팩션이 완료되면 총 세그먼트 수는 동일하게 유지되고 총 추정 크기는 2.77MB로 떨어져요.
{% hint style="info" %} 쿼리 지연을 더 개선하려면 작은 세그먼트를 더 큰 세그먼트로 병합하세요. {% endhint %}